Railway Operation Simulator  v2.12.0
A railway simulator for Windows
TrainUnit.cpp
Go to the documentation of this file.
1 // TrainUnit.cpp
2 /*
3  BEWARE OF COMMENTS in .cpp files: they were accurate when written but have
4  sometimes been overtaken by changes and not updated
5  Comments in .h files are believed to be accurate and up to date
6 
7  This is a source code file for "railway.exe", a railway operation
8  simulator, written originally in Borland C++ Builder 4 Professional with
9  later updates in Embarcadero C++Builder 10.2.
10  Copyright (C) 2010 Albert Ball [original development]
11 
12  This program is free software: you can redistribute it and/or modify
13  it under the terms of the GNU General Public License as published by
14  the Free Software Foundation, either version 3 of the License, or
15  (at your option) any later version.
16 
17  This program is distributed in the hope that it will be useful,
18  but WITHOUT ANY WARRANTY; without even the implied warranty of
19  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20  GNU General Public License for more details.
21 
22  You should have received a copy of the GNU General Public License
23  along with this program. If not, see <http://www.gnu.org/licenses/>.
24 */
25 // ---------------------------------------------------------------------------
26 #include <Classes.hpp>
27 #include <Controls.hpp>
28 #include <StdCtrls.hpp>
29 #include <Forms.hpp>
30 #include <Buttons.hpp>
31 #include <ExtCtrls.hpp>
32 #include <Menus.hpp>
33 #include <Dialogs.hpp>
34 #include <Graphics.hpp>
35 #include <ComCtrls.hpp>
36 #include <fstream>
37 #include <vector>
38 #include <algorithm> //for sort
39 #include <vcl.h>
40 #include <stdlib.h> //for rand()
41 #include <math.hpp> //for speed & performance calcs
42 
43 #pragma hdrstop
44 
45 #include "TrainUnit.h"
46 #include "TrackUnit.h"
47 #include "GraphicUnit.h"
48 #include "DisplayUnit.h"
49 #include "Utilities.h"
50 
51 // ---------------------------------------------------------------------------
52 #pragma package(smart_init)
53 
55 
56 // ---------------------------------------------------------------------------
57 
58 int TTrain::NextTrainID = 0; // has to be initialised outside the class
59 
60 // ---------------------------------------------------------------------------
61 
62 TExitInfo::TExitInfo() //default constructor
63 {
64  ServiceReference = " ";
65  RepeatNumber = 0;
66  TimeToExitSecs = -1;
67 }
68 
69 // ---------------------------------------------------------------------------
70 
71 TTrain::TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeedIn, int MassIn, double MaxRunningSpeedIn,
72  double MaxBrakeRateIn, double PowerAtRailIn, TTrainMode TrainModeIn, TTrainDataEntry *TrainDataEntryPtrIn, int RepeatNumberIn, int IncrementalMinutesIn,
73  int IncrementalDigitsIn, int SignallerMaxSpeedIn) : RearStartElement(RearStartElementIn), RearStartExitPos(RearStartExitPosIn), HeadCode(InputCode),
74  StartSpeed(StartSpeedIn), Mass(MassIn), MaxRunningSpeed(MaxRunningSpeedIn), MaxBrakeRate(MaxBrakeRateIn), PowerAtRail(PowerAtRailIn),
75  TrainMode(TrainModeIn), TrainDataEntryPtr(TrainDataEntryPtrIn), RepeatNumber(RepeatNumberIn), IncrementalMinutes(IncrementalMinutesIn),
76  IncrementalDigits(IncrementalDigitsIn), SignallerMaxSpeed(SignallerMaxSpeedIn)
77 /*
78  Construct a new train with general default values and input values for position and headcode.
79  Create the frontcode, headcode and background graphics here but don't delete them in a destructor.
80  This is because trains are kept in a vector and vectors erase elements during internal operations.
81  Deletion is explicit by using a special function. Increment the static class member NextTrainID
82  after setting this train's ID.
83 */
84 
85 {
86  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TTrain," + AnsiString(RearStartElementIn) + "," +
87  AnsiString(RearStartExitPosIn) + "," + AnsiString(InputCode) + "," + AnsiString(StartSpeedIn) + "," + AnsiString(MassIn) + "," +
88  AnsiString(TrainModeIn));
89  // AutoControl = true;//all trains start in auto control
90  UpdateCounter = 0;
91  TimeTimeLocArrived = false;
92  Derailed = false;
93  DerailPending = false;
94  Crashed = false;
95  StoppedAtBuffers = false;
96  StoppedAtSignal = false;
97  StoppedAtLocation = false;
98  StoppedAfterSPAD = false;
99  StoppedWithoutPower = false; // new at v2.4.0
100  StoppedForTrainInFront = false;
101  SignallerStoppingFlag = false;
102  SignallerStopped = false;
103  SignallerRemoved = false;
104  NotInService = false;
105  HoldAtLocationInTTMode = false;
106  AllowedToPassRedSignal = false;
107  CallingOnFlag = false;
108  BeingCalledOn = false;
109  DepartureTimeSet = false;
111  TimetableFinished = false;
112  LastActionDelayFlag = false;
113  OneLengthAccelDecel = false;
114  TrainCrashedInto = -1;
116  Plotted = false;
117  TrainGone = false;
118  SPADFlag = false;
119  FrontCodePtr = new Graphics::TBitmap;
120  FrontCodePtr->PixelFormat = pf8bit;
121  FrontCodePtr->Height = 8;
122  FrontCodePtr->Width = 8;
124  FrontCodePtr->Transparent = false;
125  AValue = sqrt(2 * PowerAtRail / Mass);
127  TerminatedMessageSent = false;
128  JoinedOtherTrainFlag = false;
130  FollowOnServiceRef = ""; //added at v2.12.0
131  TreatPassAsTimeLocDeparture = false; //added at v2.12.0
132  StepForwardFlag = false;
134  for(int x = 0; x < 4; x++)
135  {
136  HeadCodeGrPtr[x] = new Graphics::TBitmap;
137  HeadCodeGrPtr[x]->PixelFormat = pf8bit;
138  HeadCodeGrPtr[x]->Height = 8;
139  HeadCodeGrPtr[x]->Width = 8;
141  HeadCodeGrPtr[x]->Transparent = false;
142  }
143  for(int x = 0; x < 4; x++)
144  {
145  BackgroundPtr[x] = new Graphics::TBitmap;
146  BackgroundPtr[x]->PixelFormat = pf8bit;
147  BackgroundPtr[x]->Height = 8;
148  BackgroundPtr[x]->Width = 8;
150  BackgroundPtr[x]->Transparent = false;
151  }
152  for(int x = 0; x < 4; x++)
153  {
155  // set here to ensure have values
156  }
157  for(int x = 0; x < 4; x++)
158  {
159  PlotElement[x] = -1; // marker for not plotted yet
160  }
161  for(int x = 0; x < 3; x++)
162  {
163  OldZoomOutElement[x] = -1; // marker for not plotted yet
164  }
166  NextTrainID++;
167 
168  // new values added to complete initialisation of all TTrain variables
169 
170  // ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)); can't be initialised yet as session trains created with Null
171  // TrainDataEntryPtr, initialise in AddTrain
173  FrontElementLength = 0;
174  EntrySpeed = 0;
175  ExitSpeedHalf = 0;
176  ExitSpeedFull = 0;
177  MaxExitSpeed = 0;
178  BrakeRate = 0;
180  FirstHalfMove = true;
181  EntryTime = 0;
182  ExitTimeHalf = 0;
183  ExitTimeFull = 0;
184  ReleaseTime = 0;
185  TRSTime = 0;
186  LastActionTime = 0;
187  Straddle = MidLag;
188  LeadElement = -1;
189  LeadEntryPos = 0;
190  LeadExitPos = 0;
191  MidElement = -1;
192  MidEntryPos = 0;
193  MidExitPos = 0;
194  LagElement = -1;
195  LagEntryPos = 0;
196  LagExitPos = 0;
197  TrainFailed = false; // added at v2.4.0
198  for(int x = 0; x < 4; x++)
199  {
200  HOffset[x] = 0;
201  VOffset[x] = 0;
202  PlotEntryPos[x] = 0;
203  }
204  OpTimeToAct = 60; // default value, new at v2.2.0
205  TimeToExit = -1;
206  ExitPair.first = -1;
207  ExitPair.second = -1;
208  MinsDelayed = 0.0; // new at v2.2.0
209  FirstLaterStopRecoverableTime = 0.0; // new at v2.2.0
210  FinishJoinLogSent = false;
211  // added at v2.4.0 to prevent repeatdly logging the event
214  // added at v2.4.0, no need to include in session file as will only be sent once & better that way
218  ZeroPowerNoCDTMessage = false;
223  TrainFailurePending = false;
224  SkippedDeparture = false;
225  ActionsSkippedFlag = false;
226  SkipPtrValue = 0;
227  TrainSkippedEvents = 0;
228  Utilities->CallLogPop(648);
229 }
230 
231 // ---------------------------------------------------------------------------
232 
233 void TTrain::DeleteTrain(int Caller)
234 /*
235  Delete train heap objects (bitmaps) explicitly by this special function rather than by a destructor, because vectors
236  erase elements during internal operations & if TTrain had an explicit destructor that deleted the heap elements then
237  it would be called when a vector element was erased. Calling the default TTrain destructor doesn't matter because all that
238  does is release the memory of the members (including pointers to the bitmaps), it doesn't destroy the bitmaps themselves.
239  It's important therefore to call this function before erasing the vector element, otherwise the pointers to the bitmaps
240  would be lost and the bitmaps never destroyed, thereby causing memory leaks.
241  No need to delete HeadCodePosition as that just points to existing bitmaps
242 */{
243  // if(NoDelete) return;//used when a TTrain is created to hold copied values from elsewhere
244  TrainController->LogEvent("" + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
245  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DeleteTrain," + HeadCode);
246  if(Display->ZoomOutFlag)
247  {
249  }
250  if(FrontCodePtr == 0)
251  {
252  throw Exception("Error in attempting to delete FrontCodePtr");
253  }
254  delete FrontCodePtr;
255  FrontCodePtr = 0;
256  for(int x = 0; x < 4; x++)
257  {
258  if(BackgroundPtr[x] == 0)
259  {
260  throw Exception("Error in attempting to delete BackgroundPtr[" + AnsiString(x) + "]");
261  }
262  delete BackgroundPtr[x];
263  BackgroundPtr[x] = 0;
264  }
265  for(int x = 0; x < 4; x++)
266  {
267  if(HeadCodeGrPtr[x] == 0)
268  {
269  throw Exception("Error in attempting to delete HeadCodeGrPtr[" + AnsiString(x) + "]");
270  }
271  delete HeadCodeGrPtr[x];
272  HeadCodeGrPtr[x] = 0;
273  }
274  Utilities->CallLogPop(649);
275 }
276 
277 // ---------------------------------------------------------------------------
278 
280 /*
281  Plots the train starting position on screen. Note that the check for starting on straight points &
282  on wrongly set points is carried out in TrainControllerUnit [but have to allow for starting on points because
283  ChangeDirection calls this function.]. Train starts on Lead & Mid elements & Straddle = LeadMid unless
284  entering at a continuation in which case Straddle = MidLag & train not plotted immediately.
285  Set the headcode graphics pointers from the headcode text, then check whether starting at a
286  continuation. If so set Mid & Lag elements to -1 so they won't be plotted, and set Lead values
287  for the continuation element. Otherwise set Lead and Mid values,
288 
289  and Lead element value unless
290  Mid element is a buffer or continuation. Set Straddle, then for the Mid element set the graphic
291  offsets and headcode positions and front code. Pick up background bitmaps for the Mid element,
292  then check if a train on either Mid or Lag and if so give a warning message and return false so
293  that the calling function can delete the train. Plot the Mid element train values then do similarly
294  for the Lag element - set offsets, pick up background bitmaps, and plot the rear two segments of
295  the train. Finally set the Plotted flag and return true.
296 */{
297  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotStartPosition," + HeadCode);
298  int NextElementPosition, NextEntryPos, ElementLength, SpeedLimit;
299 
301  // PlotStartTime = TrainController->TTClockTime;
302  FirstHalfMove = true;
303 
304  // if enter at continuation then don't plot anything at start, but set TrainIDOnElement for continuation entry so as to
305  // 'claim' it for this train to prevent any other waiting trains trying to enter
307  {
308  LagElement = -1; // not to be plotted
309  LagExitPos = 0; // not to be plotted
310  LagEntryPos = 0; // not to be plotted
311  MidElement = -1; // not to be plotted
312  MidExitPos = 0; // not to be plotted
313  MidEntryPos = 0; // not to be plotted
315  LeadExitPos = 1; // will be 1 for continuation entry
316  LeadEntryPos = 0;
317 
319  MaxExitSpeed = StartSpeed; // initial value
321  ElementLength = Track->TrackElementAt(164, LeadElement).Length01;
322  SpeedLimit = Track->TrackElementAt(165, LeadElement).SpeedLimit01;
323  if(EntrySpeed > SpeedLimit)
324  {
325  EntrySpeed = SpeedLimit;
326  }
328  {
330  }
332  // LeadElement is the element to be entered
333 
334  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
335  // can achieve ExitSpeedFull at the half braking rate.
337  {
338  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength)); // half braking
339  if(TempEntrySpeed < EntrySpeed)
340  {
341  EntrySpeed = TempEntrySpeed;
343  }
344  }
345  Straddle = MidLag; // only for starting on a continuation
347  // no need to stop gap flashing if start on continuation
348  }
349  else // not starting at a continuation
350  {
351  LagElement = -1;
352  LagEntryPos = 0;
353  LagExitPos = 0;
360 
362  MaxExitSpeed = StartSpeed; // initial value
364  bool TempDerail = false; // dummy
365  NextElementPosition = Track->TrackElementAt(168, LeadElement).Conn[Track->GetAnyElementOppositeLinkPos(2, LeadElement, LeadEntryPos, TempDerail)];
367  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
368  {
369  StoppedWithoutPower = true;
370  }
371  // facing buffers check - ignore starting speed if start facing buffers
372  StoppedAtBuffers = false;
373  // need to set here as well as in UpdateTrain() in case paused during signaller change direction
376  {
377  FrontElementSpeedLimit = Track->TrackElementAt(494, LeadElement).SpeedLimit01; // use 01 for convenience, not used
378  FrontElementLength = Track->TrackElementAt(495, LeadElement).Length01; // use 01 for convenience, not used
379  EntrySpeed = 0;
380  ExitSpeedHalf = 0;
381  ExitSpeedFull = 0;
382  MaxExitSpeed = 0;
383  // SetTrainMovementValues not called so set this here
384  BrakeRate = 0;
387  StoppedAtSignal = false;
388  // new v2.2.0: can't be at buffers and signal! If was set then won't be reset as later
389  // signal check is an 'else'
390  if(!StoppedAtLocation)
391  {
392  StoppedAtBuffers = true; // stopped at location takes precedence
393  }
394  }
395 
396  // facing continuation check - don't allow to stop even if no power
398  {
399  FrontElementSpeedLimit = Track->TrackElementAt(509, LeadElement).SpeedLimit01; // use 01 for convenience, not used
400  FrontElementLength = Track->TrackElementAt(510, LeadElement).Length01; // use 01 for convenience, not used
404  BrakeRate = 0;
405  ExitTimeHalf = TrainController->TTClockTime + TDateTime(1.8 * (double) FrontElementLength / EntrySpeed / 86400);
406  ExitTimeFull = TrainController->TTClockTime + TDateTime(3.6 * (double) FrontElementLength / EntrySpeed / 86400);
407  }
408 
409  // Signal check
410  else if((NextElementPosition > -1) && (NextEntryPos > -1))
411  // condition check added as precaution after SloughIECC error reported by James U
412  {
413  if((Track->TrackElementAt(170, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
414  (Track->TrackElementAt(171, NextElementPosition).Attribute == 0) && !StoppedWithoutPower)
415  {
416  FrontElementSpeedLimit = Track->TrackElementAt(172, LeadElement).SpeedLimit01; // use 01 for convenience, not used
417  FrontElementLength = Track->TrackElementAt(173, LeadElement).Length01; // use 01 for convenience, not used
418  EntrySpeed = 0;
419  ExitSpeedHalf = 0;
420  ExitSpeedFull = 0;
421  MaxExitSpeed = 0;
422  BrakeRate = 0;
425  if(!StoppedAtLocation) //if it is stopped at location then don't want StoppedAtSignal until departure time if still red then, & UpdateTrain takes care of thet
426  {
427  StoppedAtSignal = true;
429  // TrainController->LogActionError(39, HeadCode, "", SignalHold, Track->TrackElementAt(754, NextElementPosition).ElementID);
430  }
432  {
433  // set both StoppedAtLocation & StoppedAtSignal, so that 'pass red signal' is offered in popup menu rather than move
434  // forwards, but don't change the background colour so still shows as stopped at location
435  StoppedAtSignal = true;
436  }
437  }
438  else
439  {
440  StoppedAtSignal = false;
441  if(NextEntryPos > 1)
442  {
443  ElementLength = Track->TrackElementAt(174, NextElementPosition).Length23;
444  SpeedLimit = Track->TrackElementAt(175, NextElementPosition).SpeedLimit23;
445  }
446  else
447  {
448  ElementLength = Track->TrackElementAt(176, NextElementPosition).Length01;
449  SpeedLimit = Track->TrackElementAt(177, NextElementPosition).SpeedLimit01;
450  }
451  if(EntrySpeed > SpeedLimit)
452  {
453  EntrySpeed = SpeedLimit;
454  }
456  {
458  }
460  TDateTime TestTime = TrainController->TTClockTime; // test
461  AnsiString TimeString = Utilities->Format96HHMMSS(TestTime); // test
462  SetTrainMovementValues(2, NextElementPosition, NextEntryPos);
463  // NextElement is the element to be entered
464 
465  // Precautionary check - If need to brake EntrySpeed may be too high, so set it to the speed at which
466  // can achieve ExitSpeedFull at the half braking rate.
468  {
469  double TempEntrySpeed = sqrt((MaxExitSpeed * MaxExitSpeed) + (3.6 * 3.6 * MaxBrakeRate * ElementLength));
470  // half braking
471  if(TempEntrySpeed < EntrySpeed)
472  {
473  EntrySpeed = TempEntrySpeed;
474  SetTrainMovementValues(3, NextElementPosition, NextEntryPos);
475  }
476  }
477  }
478  }
480  {
481  throw Exception("Error, LeadElement Exit Connection is NotSet");
482  }
483  }
484  if(MidElement > -1) // will be -1 if start on continuation
485  {
486  Straddle = LeadMid;
490  {
491  for(int x = 0; x < 4; x++)
492  {
493  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
494  }
495  }
496  else
497  {
498  for(int x = 0; x < 4; x++)
499  {
501  }
502  }
503  if(TrainMode == Timetable)
504  {
506  }
507  else
508  {
510  }
512  // pick up background bitmaps [0] & [1] & plot HeadCodes [0] & [1]
513 
516 /* Move check to AddTrain, also, now that can start on bridges need to check that other train is on same track before refusing
517  if((Track->TrackElementAt(182, LeadElement).TrainIDOnElement > -1) || ((MidElement > -1) && (Track->TrackElementAt(183, MidElement).TrainIDOnElement > -1)))
518  {
519  ShowMessage("Can't place train " + HeadCode + "; another train already present at location");
520  Utilities->CallLogPop(651);
521  return false;
522  }
523 */
528  PlotTrainGraphic(8, 0, Display);
529  PlotTrainGraphic(9, 1, Display);
530 
533 
534  // pick up background bitmaps [2] & [3]
535 
538 
539  PlotElement[2] = MidElement;
541  PlotElement[3] = MidElement;
543  PlotTrainGraphic(10, 2, Display);
544  PlotTrainGraphic(11, 3, Display);
545  // Plotted = true; set in PlotTrainGraphic
546  }
547  Display->Update(); // resurrected when Update() dropped from PlotOutput etc
548  Utilities->CallLogPop(652);
549 }
550 
551 // ---------------------------------------------------------------------------
552 void TTrain::UnplotTrain(int Caller)
553 {
554  // Note: If trouble is experienced with the PlotAlternativeTrackRouteGraphic functions remove them & test for train on a bridge and if so call Clearand..
555  if(!Plotted)
556  {
557  return;
558  }
559  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrain," + HeadCode);
560 
561  if(Straddle == MidLag)
562  {
563  if(MidElement > -1)
564  {
569  // to force plot of locked route marker, needed once only for the element
570  }
571  if(LagElement > -1)
572  {
577  // to force plot of locked route marker, needed once only for the element
578  }
579  }
580  else if(Straddle == LeadMidLag)
581  {
582  if(LeadElement > -1)
583  {
586  // to force plot of locked route marker, needed once only for the element
587  }
588  if(MidElement > -1)
589  {
594  // to force plot of locked route marker, needed once only for the element
595  }
596  if(LagElement > -1)
597  {
600  // to force plot of locked route marker, needed once only for the element
601  }
602  }
603  else if(Straddle == LeadMid)
604  {
605  if(LeadElement > -1)
606  {
611  // to force plot of locked route marker, needed once only for the element
612  }
613  if(MidElement > -1)
614  {
619  // to force plot of locked route marker, needed once only for the element
620  }
621  }
622  if(LeadElement > -1)
623  {
625  }
626  if(MidElement > -1)
627  {
629  }
630  if(LagElement > -1)
631  {
633  }
634  Plotted = false;
636  Display->Update();
637  // without this the screen 'blinks' at next Clearand... prob forces a full repaint for some reason
638  // resurrected when Update() dropped from PlotOutput etc
639  Utilities->CallLogPop(653);
640 }
641 
642 // ----------------------------------------------------------------------------
643 
644 void TTrain::UpdateTrain(int Caller)
645 /*
646  Note: Some changes made since comments written
647 
648  Brief:
649  Enter with Straddle defining train position wrt Lag, Mid & Lead elements. Is only MidLag at this point
650  on first entry at a continuation (with no train plotted), in all other cases it is either LeadMid (when train fully
651  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
652  Thereafter on entry Straddle = LeadMidLag or LeadMid; LeadMid if train fully on Mid & Lead elements, and
653  LeadMidLag if on Lag, Mid and Lead elements (back on lag, front on Lead, & middle 2 segments on Mid).
654  If enter with Straddle = LeadMid, then train is in effect in the first half of the next element, and moves half onto it after
655  the half time point has been passed. The values for the next element were set when the train was last updated when Straddle became
656  LeadMid from LeadMidLag. After the half time point has been passed Straddle is
657  changed to MidLag within the function and all elements moved down one, old Mid becomes
658  the new lag, old Lead becomes the new Mid, and a new Lead is obtained. Then the new positions are plotted, and finally Straddle is
659  incremented to reflect the position the train now occupies.
660 
661  Detail:
662  Set TrainFailurePending if all conditions met
663  Check whether stopped at a non-red signal, and if so reset StoppedAtSignal so train can move.
664  Check whether buffers at immediate exit, either when first enter the function or later, and set StoppedAtBuffers if so
665  and return.
666  If Straddle == LeadMid then train fully on Lead and Mid, so ready for a major update:-
667  If there's a LagElement (there will be but include check for good practice - next
668  function depends on it) Check whether DerailPending set - set during last GetLeadElement if appropriate but only acted on here when
669  train fully on offending point - Derail set and DerailPanding reset, train background
670  colour changed (note that BackgroundColour is a property of the train itself) then return.
671  If no derail pending reset Lag and Mid elements to the old Mid and Lead values, reset Straddle to MidLag, then set
672  the new LeadElement, which will be the next connected element (obtain using GetLeadElement) or -1 if the current
673  LeadElement is an exit continuation. During GetLeadElement the element at LeadElement is checked and if a stop
674  signal is found StoppedAtSignal is set to true, otherwise StoppedAtSignal is set to false. Also Derail is set
675  if LeadElement is a fouled trailing point.
676  Now, the train is moved on by one segment. Firstly the last BackgroundElement is set to LagElement, then the last
677  segment of the LagElement is unplotted (if there is a LagElement - may be entering at a continuation), by
678  replotting the last background segment and checking whether the element is a bridge or crossover with the other
679  track in a route, in which case the route colour is replotted.
680  Then, if Straddle == LeadMidLag (train will move completely off the element during this function), and the train
681  track is in a route, then all the train elements are removed from the route unless it's an autosig route. Normally only the
682  LeadElement will be in a route for a moving train, but when originally placed all elements may be in the route so check them all.
683  Note also that there may be two routes at a given element position, but only one of them is the correct one, so this
684  is identified prior to the removal. Also the TrainIDs are reset because the train will be fully off this element at the end of
685  the function. If Straddle == LeadMidlag and the element being left is a ContinuationExit the the TrainGone flag is set so the
686  train can be deleted by the calling function, and the function returns.
687  If the element is a signal in the train movement direction, then it is reset to red (Attribute = 0) and is replotted
688  to show the red aspect. Finally if element is a signal in the other direction it is replotted as it was - need to
689  plot individually because could have any aspect, the background bitmap that was picked up earlier contains just the
690  basic red aspect.
691 
692  Now all the array values are updated, but the [0] values are as yet invalid, these have to be obtained explicitly from
693  the new LeadElement later. The headcode graphics are updated so that it reads correctly - left to right & top to bottom,
694  regardless of direction, and with the correct front code colour.
695 
696  The new front segment background bitmap is now picked up and the graphic offsets set, and the segments are plotted.
697  No more unplotting is needed as all but the last segment are overwritten by later segments, and the new front
698  segment is just plotted, though the background bitmap at that location has to be picked up. Just where they are
699  plotted depends on the Straddle value, [0] is always on Lead, [1] is on Lead if Straddle == LeadMidLag or Mid if
700  Straddle == MidLag; [2] is always on Mid, and [3] is on Mid if Straddle == LeadMidLag or Lag if Straddle == MidLag.
701  Also prior to plotting the lead segment a crash check is made, and if true the Crashed flag is set and the
702  TrainCrashedInto value also set to the current TrainID - this is so it too becomes crashed and hence stopped.
703 
704  The Crashed flag is now checked, and if set the front headcode colour is changed to the same as the rest of the code,
705  and the background colour changed. Then the train that is crashed into is also set to Crashed, and its colours
706  changed similarly. The function then returns.
707 
708  If Crashed is not set then Straddle is incremented and the function returns.
709 */
710 
711 {
712  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UpdateTrain," + HeadCode);
713  UpdateCounter++;
714  // 100 counts = 5secs (used in splits to prevent too frequent length checks in front & rear splits)
715  if(UpdateCounter >= 100)
716  {
717  UpdateCounter = 0;
718  }
719  int RandRange = (TrainController->MTBFHours * 3600) / 53;
720 
721  // MTBFHours is in timetable clock hours, min value is 1 & max value is 10,000 (integer values on input)
722  // but double on use because it represents timetable clock time, so at 1/16 speed RandRange is * 16 (160,000 max) & at 16x speed its /16 (1/16 min)
723  // i.e MTBFHours is Input value/TTClockSpeed (conversion is done in InterfaceUnit)
724  if(int(TrainController->RandomFailureCounter) == (rand() % 1060))
725  // RandomFailureCounter value is fixed for a full cycle of train updates so this
726  // makes sure there's no bunching of failures as there is for a fixed comparison number
727  // or a small range of comparison numbers. True every 53 secs (real time) on average rand()
728  // gives a random number between 0 and 32767 (defined as RAND_MAX in stdlib.h)
729  {
730  if(!TrainFailed && !TrainOnContinuation(0) && (RandRange > 0) && (PowerAtRail > 1) && !((TrainMode == Timetable) && TimetableFinished)
731  && !Crashed && !Derailed && !((TrainMode == Signaller) && Stopped()))
732  // RandomFailureCounter resets to 0 every 53 secs, if RandRange is 0 then no failure rate is set - i.e. failure rate = 0
733  // don't fail if:
734  // (a) on a continuation (entering or leaving);
735  // (b) already failed;
736  // (c) power effectively zero (8000) min value for powered; 0.08 for 'no power';
737  // (d) train terminated;
738  // (e) crashed or derailed; or
739  // (f) under signaller control and stopped.
740  // (g) TeatPassAsTimeLocDeparture is true //added at v2.12.0
741  {
742  if(((random(RandRange)) == 0) && !TreatPassAsTimeLocDeparture)
743  // max value for RandRange is over 2x10^9
744  {
745  // here if failure due
746  TrainFailurePending = true;
747  // fail when PlotElements set to proper Lead & Mid Elements
748  }
749  }
750  }
751 /* dropped as it allows a train to stop on a half element - when reach (if Stopped()) at line 1310
752  if ((PowerAtRail < 1) && (EntrySpeed < 1)) // added at v2.4.0
753  {
754  StoppedWithoutPower = true;
755  }
756 */
757 // float TimeToExit; //added at v2.10.0 Removed these so original values retained - used when train on continuation
758 // THVShortPair ExitPair; //added at v2.10.0
759  int LockedVectorNumber;
760  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
761  // default values - these needed for route checker below
762  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
763 
765  {
767  }
768  if(Crashed || Derailed)
769  {
771  {
772  PlotTrain(7, Display);
773  // replotted every cycle because of level crossing crashes, otherwise a flashing level crossing wipes out half of the train
774  Display->Update();
775  }
776  OpTimeToAct = 0.0;
777  // need to set this here as wouldn't be calculated otherwise as return from UpdateTrain
778  Utilities->CallLogPop(1017);
779  return; // no further action, user has to remove or work around
780  }
782  {
784  }
786  {
788  }
790  // introduced at v1.2.0, formerly 'TimeTimeLocArrived = false' was included
791  // in the next condition 'if(!Stopped() && !SPADFlag)' which led to repeated arrival messages if signaller control allowed a train
792  // to move & then stop again at the same station
793  {
794  TimeTimeLocArrived = false;
795  }
796  if(!Stopped() && !SPADFlag && !TrainFailed)
797  {
799  }
800  // set or release StoppedAtBuffers if fully on 2 elements depending on LeadElement
801  // Note that if LeadElement == Buffers train must be facing the buffer so no need to check orientation
802 /* old version where force a stop at buffers regardless of speed
803  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(, LeadElement).TrackType == Buffers)) StoppedAtBuffers = true;
804  else StoppedAtBuffers = false;
805 */
806 
807  // new version where crash if run into buffers
808  if(!Crashed)
809  {
810  if((Straddle == LeadMid) && (LeadElement > -1) && (Track->TrackElementAt(602, LeadElement).TrackType == Buffers))
811  {
812  if(ExitSpeedFull > 1)
813  {
814  Crashed = true;
818  // SendMissedActionLogs(3, -1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
819  // no need for missed action logs - will be sent when train removed
820  StoppedAtBuffers = false;
821  }
823  // stopped at location & stopped without power take precedence
824  {
825  StoppedAtBuffers = true;
826  }
827  else
828  {
829  StoppedAtBuffers = false;
830  }
831  }
832  else
833  {
834  StoppedAtBuffers = false;
835  }
836  }
837  else
838  {
839  StoppedAtBuffers = false;
840  }
841  // if crashed don't want stopped at buffers set
842 
843  // also crash if run into a level crossing that is changing or has barriers up
844  if(!Crashed)
845  {
846  if((Straddle == LeadMid) && (LeadElement > -1) && (ExitSpeedFull > 1))
847  {
848  int H = Track->TrackElementAt(873, LeadElement).HLoc;
849  int V = Track->TrackElementAt(874, LeadElement).VLoc;
850  if(Track->IsLCAtHV(40, H, V) && !Track->IsLCBarrierDownAtHV(2, H, V))
851  {
852  Crashed = true;
856  // no need for missed action logs - will be sent when train removed
857  }
858  }
859  }
861  {
863  }
864  // set or reset HoldAtLocationInTTMode (if true then actions are needed before train departs)
866  //if Command == "" then either TimeLoc or TimeTimeLoc so don't hold, and added last part at v2.12.0 so don't hold if have both command == pas and Treat... flag
867  {
868  HoldAtLocationInTTMode = true;
869  }
870  else if(TrainMode == Timetable)
871  {
872  HoldAtLocationInTTMode = false;
873  }
874  // in Signaller mode HoldAtLocationInTTMode not changed
875 
876  // check if departure pending & set times unless already set
877  if(TrainMode == Timetable)
878  {
880  // && !StoppedAtBuffers) - drop this, set times whether or not at buffers
881  {
882  if(ActionVectorEntryPtr->DepartureTime > TDateTime(-1))
883  {
885  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
886  {
887  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
888  }
889  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
890  DepartureTimeSet = true;
891  }
892  if((ActionVectorEntryPtr->Command == "pas") && TreatPassAsTimeLocDeparture) //new segment at v2.12.0 to treat a pass as a departure
893  {
895  if(ReleaseTime <= LastActionTime + TDateTime(30.0 / 86400))
896  {
897  ReleaseTime = LastActionTime + TDateTime(30.0 / 86400);
898  }
899  TRSTime = ReleaseTime - TDateTime(10.0 / 86400);
900  DepartureTimeSet = true;
901  }
902  }
903  }
904  if(TrainController->OpTimeToActUpdateCounter == 0)// && TrainController->OpActionPanelVisible) removed last condition so always calc TimeToExit
905  {
906  OpTimeToAct = CalcTimeToAct(0, TimeToExit, ExitPair); // called after ReleaseTime set
907 // this->TimeToExit = TimeToExit; don't need these as values updated directly
908 // this->ExitPair = ExitPair;
909  // calculate every 1 sec (in real time, not timetable time) for all trains
910  }
911  // check if being held at location pending any actions & deal with them if time appropriate & >= 30s since LastActionTime
912  if(TrainMode == Timetable)
913  {
914  if((ActionVectorEntryPtr->Command != "Frh") && (ActionVectorEntryPtr->Command != "Frh-sh"))
915  {
916  RemainHereLogNotSent = true;
917  }
919  {
920  // ignore TimeLoc & TTLoc departures
921  // Action logs given in functions
923  LastActionTime + TDateTime(30.0 / 86400)))
924  {
925  if(ActionVectorEntryPtr->Command == "fsp")
926  {
927  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
928  FrontTrainSplit(0);
929  if(TrainFailurePending) // ok, stopped so PlotElements set
930  {
931  TrainHasFailed(0);
932  }
933  Utilities->CallLogPop(2041);
934  return;
935  }
936  else if(ActionVectorEntryPtr->Command == "rsp")
937  {
938  // added for v1.3.2 because when add new train to TrainVector 'this' address likely invalidated, hence make no more changes to 'this' train. Next clock cycle will deal with any required changes
939  RearTrainSplit(0);
940  if(TrainFailurePending) // ok, stopped so PlotElements set
941  {
942  TrainHasFailed(1);
943  }
944  Utilities->CallLogPop(2042);
945  return;
946  }
947  else if(ActionVectorEntryPtr->Command == "Fjo")
948  {
949  FinishJoin(0);
950  }
951  else if(ActionVectorEntryPtr->Command == "jbo")
952  {
953  JoinedBy(0);
954  }
955  else if(ActionVectorEntryPtr->Command == "cdt")
956  {
957  ChangeTrainDirection(0, false);
958  }
959  else if(ActionVectorEntryPtr->Command == "Fns")
960  {
961  NewTrainService(0, false);
962  }
963  else if(ActionVectorEntryPtr->Command == "Frh")
964  {
965  RemainHere(0);
966  }
967  else if(ActionVectorEntryPtr->Command == "Fer")
968  {
969  TimetableFinished = true;
970  }
971  // other aspects of 'Fer' dealt with in TTrain::Update()
972  else if(ActionVectorEntryPtr->Command == "F-nshs")
973  {
975  }
976  else if(ActionVectorEntryPtr->Command == "Frh-sh")
977  {
978  RepeatShuttleOrRemainHere(0, false);
979  }
980  else if(ActionVectorEntryPtr->Command == "Fns-sh")
981  {
983  }
984 /*
985  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
986  shuttle headcode (no train creation)
987  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
988  remain here
989  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done
990  form new service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
991 */
992  }
993  }
994  else
995  {
997  {
999  }
1000  }
1001  }
1002  if(TrainMode == Timetable)
1003  {
1004  if(StoppedAtBuffers)
1005  {
1006  // error if buffers (& element before it) not at a location, or if buffer location different to ActionVectorEntryPtr location
1007  // if buffer location same as ActionVectorEntryPtr location & not Frh then error will be given for inability to depart
1008  AnsiString BufferLocation = Track->TrackElementAt(604, LeadElement).ActiveTrackElementName;
1009  if(BufferLocation == "")
1010  {
1012  }
1013  AnsiString ExpectedLocation = ActionVectorEntryPtr->LocationName;
1014  if((BufferLocation == "") || (BufferLocation != ExpectedLocation))
1015  {
1019  {
1021  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1022  // Drop missed actions so user can still use sig mode to get back on track
1024  }
1025  if(TrainFailurePending) // ok, stopped so PlotElements set
1026  {
1028  TrainHasFailed(2);
1029  }
1030  Utilities->CallLogPop(1020);
1031  return;
1032  }
1033  else if((BufferLocation != "") && (BufferLocation == ExpectedLocation) && DepartureTimeSet && !RevisedStoppedAtLoc() && (TrainController->TTClockTime >
1034  ReleaseTime))
1035  {
1038  {
1041  // SendMissedActionLogs(-1, ActionVectorEntryPtr);//-1 is a marker for send messages for all remaining entries, including Fer if present
1042  // Drop missed actions so user can still use sig mode to get back on track
1044  }
1045  if(TrainFailurePending) // ok, stopped so PlotElements set
1046  {
1048  TrainHasFailed(3);
1049  }
1050  Utilities->CallLogPop(1397);
1051  return;
1052  }
1053  }
1054  else
1055  {
1057  }
1058  }
1059  else
1060  {
1062  }
1063  if(TrainMode == Timetable)
1064  {
1066  {
1068  }
1070  {
1072  }
1073  }
1074  // Pick up element next to the train front (if exists) to check for calling-on, restart after a cleared signal, or
1075  // restart after stopped for train in front
1076  int NextElementPosition, NextEntryPos;
1077 
1078  if(LeadElement > -1) // if an exit continuation then not set
1079  {
1080  if((Track->TrackElementAt(186, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1081  {
1083  }
1084  else if((Track->TrackElementAt(187, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1085  {
1086  if(Track->TrackElementAt(188, LeadElement).Attribute == 0)
1087  {
1088  LeadExitPos = 1;
1089  }
1090  else
1091  {
1092  LeadExitPos = 3;
1093  }
1094  }
1095  NextElementPosition = Track->TrackElementAt(189, LeadElement).Conn[LeadExitPos];
1096  NextEntryPos = Track->TrackElementAt(190, LeadElement).ConnLinkPos[LeadExitPos];
1097  }
1098  else
1099  {
1100  NextElementPosition = -1;
1101  NextEntryPos = -1;
1102  }
1103  if((NextElementPosition > -1) && (NextEntryPos > -1))
1104  // may be buffers or continuation so need this check
1105  {
1106 /*
1107  Check whether calling-on conditions met:-
1108  a) approaching train has stopped at a signal but not at a location;
1109  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
1110  change of direction (cdt), remaining here (Frh), or under signaller control);
1111  c) at least 1 platform available for the approaching train;
1112  d) points (if any) set for direct route into platform;
1113  e) approaching train is to stop at station;
1114  f) no more facing signals between train and platform;
1115  g) [dropped g]
1116  h) train in front preventing route being set far enough to release stop signal;
1117  i) train in front not exiting at continuation;
1118  j) signal must be within 4km of the stop platform;
1119  k) [dropped (k), now can set a reoute or part route into platform so can set points more easily];
1120  l) no existing route conflicts with the route into the platform; and
1121  m) not failed or without power (these added at v2.10.0)
1122  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or
1123  other route conflicts - if a partial route set than can still change points outside the route or have a route conflict if another route is set.
1124 */
1125  if(TrainMode == Timetable)
1126  {
1127  if(CallingOnAllowed(0)) //returns false if failed or no power (modified afer v2.9.2)
1128  {
1129  CallingOnFlag = true;
1130  PlotTrainWithNewBackgroundColour(1, clCallOnBackground, Display); // calling-on background
1131  }
1132  else
1133  {
1134  if(CallingOnFlag) //TrainHasFailed sets this flag to false (at v2.10.0)
1135  {
1136  if(!TrainFailed) //shouldn't be needed but include for safety at v2.10.0
1137  {
1139  }
1140  }
1141  CallingOnFlag = false;
1142  }
1143  }
1144  if(StoppedAtSignal && ((Track->TrackElementAt(191, NextElementPosition).Attribute > 0) || AllowedToPassRedSignal) && !TrainFailed && !RevisedStoppedAtLoc())
1145  {
1146  //'&& !StoppedAtLocation' added at v2.7.0 as if had been stopped at signal before tt control restored then background colour changed to normal when signal changed from red
1147  // reset PassRedSignal when reached half-way point in next element, if reset here then SetTrainMovementValues
1148  // sets StoppedAtSignal again & train doesn't move
1149  StoppedAtSignal = false;
1150  // need to recalculate exit times since old entry time expired. Straddle now at MidLag with front of train on MidElement
1151  // hence use MidElement for the calculation so same as would have been used if signal not red, when Straddle was
1152  // LeadMidLag and front of train was on LeadElement (after the current move)
1154  EntrySpeed = 0;
1156  FirstHalfMove = true;
1157  SetTrainMovementValues(4, NextElementPosition, NextEntryPos);
1158  // NextElement is the element to be entered
1159  }
1161  {
1162  if(ClearToNextSignal(0))
1163  {
1164  StoppedForTrainInFront = false;
1165  BeingCalledOn = false;
1166  EntrySpeed = 0;
1168  FirstHalfMove = true;
1169  SetTrainMovementValues(16, NextElementPosition, NextEntryPos);
1170  }
1171  else
1172  {
1173  if(TrainFailurePending) // ok, stopped so PlotElements set
1174  {
1175  TrainHasFailed(4);
1176  }
1177  Utilities->CallLogPop(1097);
1178  return;
1179  }
1180  }
1181  }
1182  if((Straddle == MidLag) && (LeadElement != -1))
1183  // later check only for Straddle == LeadMid, so need this check here for initial train start
1184  {
1186  }
1187 /* Logic below as follows: This check is made to allow a restart if had StoppedAtLocation or StoppedForTrainInFront or
1188  both but potentially able to restart (i.e. not at buffers, not crashed, not derailed, not held at location, departure
1189  time due, no train in front now & no other stop condition). Note that can be StoppedForTrainInFront when not at a
1190  location since this is set in SetTrainMovementValues whenever a train has zero EntrySpeed and there is a train in front,
1191  which could be when start as Snt.
1192  If StoppedForTrainInFront but not StoppedAtLocation then need to set TRSTime high so pink not plotted, and ReleaseTime
1193  low so can restart if appropriate. BeingCalledOn was set so that when train stopped at a station it wouldn't restart
1194  until the line was clear of trains up to the next signal. Hence check whether BeingCalledOn & if so set
1195  StoppedForTrainInFront, this ensures two things - that the restart check is carried out at each cycle and also that
1196  a restart won't happen until the line is clear to the next signal, regardless of whether or not the ReleaseTime has been
1197  reached.
1198  Then check if TRS time reached & change background to pink if so, & check if release time reached & if so change
1199  background to white and clear StoppedAtLocation. Then make check of station name, and recheck StoppedForTrainInFront,
1200  if it's set check if ClearToNextSignal and if so clear StoppedForTrainInFront & BeingCalledOn. If not ClearToNextSignal
1201  then return. If either not StoppedForTrainInFront or ClearToNextSignal then restart, calling SetTrainMovementValues &
1202  sending a message to the performancelog.
1203 */
1204 
1205  if(TrainMode == Timetable)
1206  {
1208  {
1209  if(BeingCalledOn)
1210  {
1211  StoppedForTrainInFront = true;
1212  }
1214  {
1216  }
1218  {
1219  // value updated at every scheduled departure & arrival
1221  AnsiString StationName;
1223  {
1225  }
1227  {
1229  }
1230  else
1231  {
1232  throw Exception("Error - Stopped at through station but neither lead nor mid elements have a name");
1233  }
1234  EntrySpeed = 0;
1238  FirstHalfMove = true;
1239  StoppedAtLocation = false;
1240 
1241  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1242  {
1243  StoppedWithoutPower = true;
1244  }
1245  if((NextElementPosition > -1) && (NextEntryPos > -1))
1246  // condition check added for SloughIECC error reported by James U
1247  {
1248  if((Track->TrackElementAt(720, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1249  (Track->TrackElementAt(721, NextElementPosition).Attribute == 0))
1250  {
1251  StoppedAtSignal = true;
1252  if(!StoppedWithoutPower)
1253  // if stopped without power just keep existing background colour
1254  {
1256  // TrainController->LogActionError(40, HeadCode, "", SignalHold, Track->TrackElementAt(755, NextElementPosition).ElementID);
1257  }
1258  }
1259  }
1261  {
1262  TimeTimeLocArrived = false;
1263  LogAction(27, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->DepartureTime, false);
1264  // no warning for TimeTimeLoc departure
1265  }
1266  else if(TreatPassAsTimeLocDeparture) //added at v2.12.0 so late/early/on time mins recorded accurately
1267  {
1268  LogAction(36, HeadCode, "", Depart, StationName, ActionVectorEntryPtr->EventTime, ActionVectorEntryPtr->Warning); //EventTime because the real event is a pass
1269  }
1270  else //must be TimeLoc departure
1271  {
1273  }
1274  TreatPassAsTimeLocDeparture = false; //added at v2.12.0, reset after train departs
1275  DepartureTimeSet = false;
1276  // no need to set LastActionTime for a departure
1277  //deal here with departure pointer change, increment if SkippedDeparture
1278  if(SkippedDeparture)
1279  {
1282  TrainSkippedEvents = 0;
1283  SkippedDeparture = false;
1284  SkipPtrValue = 0;
1285  ActionsSkippedFlag = false;
1286  }
1287  else
1288  {
1290  }
1291  // advance pointer beyond departure action - (this line (& LogAction) used to be at the end - see
1292  // note
1293 /*
1294  Note: If train stops at station after call on with a TimeTimeLoc loaded, and before the normal stop point, then when
1295  SetTrainMovementValues called it assumes a stop at the stop point because the ActionVectorEntryPtr points to a name
1296  when NameInTimetableBeforeCDT is called and the stop positions are valid. So next element train movement is based on
1297  this calculation. However, when the departure time check is made (it is during this function when SetTrainMovementValues
1298  is called), the ActionVectorEntryPtr is advanced at the end past the departure location, so at the next element when
1299  SetTrainMovementValues is called again, all is normal, i.e. the train doesn't stop again at the location. But to cure
1300  the problem move the ActionVectorEntryPtr increment to before SetTrainMovementValues.
1301 */
1303  {
1304  StoppedAtBuffers = true;
1305  }
1306  else if(!StoppedWithoutPower)
1307  // if buffers or no power, don't set values
1308  {
1310  {
1311  SetTrainMovementValues(12, NextElementPosition, NextEntryPos);
1312  // NextElement is the element to be entered
1313  }
1314  else
1315  {
1317  // use LeadElement for an exit continuation
1318  }
1319  }
1320  }
1321  }
1322  }
1323  if(Straddle == LeadMidLag) //train on a half element
1324  {
1326  {
1327  Utilities->CallLogPop(654);
1328  return;
1329  }
1330  }
1331  else //train fully on 2 elements
1332  {
1334  {
1335  Utilities->CallLogPop(655);
1336  return;
1337  }
1338  }
1339  if((LeadElement > -1) && (MidElement > -1))
1340  {
1342  {
1343  // don't allow to stop if exiting at a continuation as causes problems if try to change direction
1344  // if entering at continuation & LeadElement is a continuation then MidElement will be -1
1345  //don't need to check for MidElement being continuation because popup menu won't show when exiting at continuation so SignallerStoppingFlag can't be set
1346  SignallerStoppingFlag = false;
1347  StepForwardFlag = false;
1348  }
1349  }
1350  if(Stopped())
1351  // this is what prevents another movement if the train is stopped
1352  {
1353  if(TrainFailurePending) // ok, stopped so PlotElements set
1354  {
1355  TrainHasFailed(5);
1356  }
1357  BrakeRate = 0;
1358  Utilities->CallLogPop(656);
1359  return;
1360  }
1361  // here when ready for next move
1362 
1363  //added at v2.10.0 to set SPADFlag if red signal immediately ahead (as it will be if in a locked route)
1364  //check if due to run past a red signal & if so set SPADFlag (SetTrainMovementValues & its SPAD check only called when arrive fully on 2 elements)
1365  if(Straddle == LeadMid) //fully on 2 elements
1366  {
1367  if(LeadElement > -1)
1368  {
1369  if(Track->TrackElementAt(1402, LeadElement).Conn[LeadExitPos] > -1)
1370  {
1372  if(TIF.TrackType == SignalPost)
1373  {
1374  int TIFEntryPos = Track->TrackElementAt(1405, LeadElement).ConnLinkPos[LeadExitPos];
1375  int TIFExitPos = 0;
1376  if(TIFEntryPos == 0)
1377  {
1378  TIFExitPos = 1;
1379  }
1380  if((TIF.Config[TIFExitPos] == Signal) && TIF.Attribute == 0 && (ExitSpeedHalf > 1) && !AllowedToPassRedSignal) //use ExitSpeedHalf as may have been stopped at signal so entryspeed is 0
1381  {
1382  SPADFlag = true; // user has to intervene to reset & restart after spad
1383  }
1384  }
1385  }
1386  }
1387  }
1388 
1389  // check for train in front & if so stop at next access (when train fully on element next to train)
1390  if((TrainMode == Signaller) && (Straddle == LeadMidLag))
1391  // SetTrainMovementValues brakes & stops signaller mode train for a train in front using local
1392  // variable TrainInFrontInSignallerModeFlag
1393  {
1394  if(LeadElement > -1)
1395  {
1396  int NextPos = Track->TrackElementAt(649, LeadElement).Conn[LeadExitPos];
1397  int NextEntryPos = Track->TrackElementAt(650, LeadElement).ConnLinkPos[LeadExitPos];
1398  if(Track->OtherTrainOnTrack(1, NextPos, NextEntryPos, TrainID))
1399  // true if another train on NextEntryPos track whether bridge or not
1400  {
1401  StoppedForTrainInFront = true;
1402  }
1403  else
1404  {
1405  StoppedForTrainInFront = false;
1406  }
1407  }
1408  }
1409  if((Straddle == LeadMid) && SPADFlag)
1410  // give message + plot background when ready to move half past the signal
1411  {
1412  if(NextElementPosition > -1)
1413  {
1414  if((Track->TrackElementAt(662, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1415  (Track->TrackElementAt(663, NextElementPosition).Attribute == 0))
1416  {
1417  AnsiString LocID = AnsiString(Track->TrackElementAt(664, NextElementPosition).ElementID);
1419  // if goes past 2 signals then give message twice
1421  }
1422  }
1423  }
1424  if(Straddle == LeadMidLag)
1425  // During this function train moves fully onto 2 elements, Lead & Mid, so set next 2 moves from here for the element after Lead
1426  {
1427  // if SPADFlag set allow to keep moving until signal obscured before setting background colour, & stop only when ExitSpeedFull is 0
1428  if(SPADFlag)
1429  {
1430  if(ExitSpeedFull == 0)
1431  {
1432  StoppedAfterSPAD = true;
1433  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1434  }
1435  }
1437  {
1438  if(ExitSpeedFull == 0)
1439  {
1440  // only reach here when will stop on LeadMid, because SetTrainMovementValues called after this (i.e. ExitSpeedFull becomes 0 if not 0 now
1441  // after this test), and Straddle == LeadMidLag so not accessed at the half-move point, hence only reached at the full move
1442  // point when the speed is 0. So, colour change won't occur until fully stopped (early in UpdateTrain()), and the log message
1443  // is sent at the right time and once only.
1444  SignallerStopped = true;
1445  // but don't want to stop until have moved fully onto element, hence stop test is before this check
1446  StepForwardFlag = false;
1447  SignallerStoppingFlag = false;
1448  TTrackElement TE;
1449  AnsiString Loc = "";
1450  bool LocNamed = false;
1451  if(LeadElement > -1)
1452  {
1453  TE = Track->TrackElementAt(782, LeadElement);
1454  if(TE.ActiveTrackElementName != "")
1455  {
1456  Loc = TE.ActiveTrackElementName;
1457  LocNamed = true;
1458  }
1459  else
1460  {
1461  Loc = "track element " + TE.ElementID;
1462  }
1463  }
1464  if((MidElement > -1) && !LocNamed)
1465  {
1466  TE = Track->TrackElementAt(783, MidElement);
1467  if(TE.ActiveTrackElementName != "")
1468  {
1469  Loc = TE.ActiveTrackElementName;
1470  LocNamed = true;
1471  }
1472  else if(Loc == "")
1473  {
1474  Loc = "track element " + TE.ElementID;
1475  }
1476  }
1477  if(Loc == "")
1478  {
1479  Loc = "outside railway";
1480  // must have stopped after left at a continuation (because both lead & mid == -1)
1481  }
1482  else
1483  {
1484  Loc = "at " + Loc;
1485  }
1486  LogAction(30, HeadCode, "", SignallerStop, Loc, TrainController->TTClockTime, false); // false for warning
1487  }
1488  }
1489  if(LeadElement > -1) // if an exit continuation then not set
1490  {
1491  if((Track->TrackElementAt(202, LeadElement).TrackType != Points) || ((LeadEntryPos != 0) && (LeadEntryPos != 2)))
1492  {
1494  }
1495  else if((Track->TrackElementAt(203, LeadElement).TrackType == Points) && ((LeadEntryPos == 0) || (LeadEntryPos == 2)))
1496  {
1497  if(Track->TrackElementAt(204, LeadElement).Attribute == 0)
1498  {
1499  LeadExitPos = 1;
1500  }
1501  else
1502  {
1503  LeadExitPos = 3;
1504  }
1505  }
1506  NextElementPosition = Track->TrackElementAt(205, LeadElement).Conn[LeadExitPos];
1507  NextEntryPos = Track->TrackElementAt(206, LeadElement).ConnLinkPos[LeadExitPos];
1508  }
1509  else
1510  {
1511  NextElementPosition = -1;
1512  NextEntryPos = -1;
1513  }
1516  FirstHalfMove = true; //will be when finished the move onto 2 elements during this function
1517 
1518  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
1519  {
1520  StoppedWithoutPower = true;
1521  }
1522  if((NextElementPosition > -1) && (NextEntryPos > -1) && !SPADFlag)
1523  // may be buffers or continuation. SPADFlag added at v2.1.0
1524  // so don't override the SPAD colour & don't set StoppedAtSignal
1525  {
1526  if((Track->TrackElementAt(207, NextElementPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal) &&
1527  (Track->TrackElementAt(208, NextElementPosition).Attribute == 0) && (ExitSpeedFull < 1) && !RevisedStoppedAtLoc())
1528  {
1529  StoppedAtSignal = true;
1530  if(!StoppedWithoutPower)
1531  // leave background as is if no power, but set StoppedAtSignal
1532  {
1534  }
1535  // TrainController->LogActionError(41, HeadCode, "", SignalHold, Track->TrackElementAt(756, NextElementPosition).ElementID);
1536  }
1537  }
1538  if(!Stopped())
1539  {
1540  if((NextElementPosition > -1) && (NextEntryPos > -1))
1541  // may be buffers or continuation (skip SetTrainMovementValues if buffers, if
1542  // a stop element that isn't buffers - e.g. station, then will skip the calcs
1543  // during SetTrainMovementValues to avoid trying to divide by zero - see that
1544  // function for fuller explanation
1545  {
1546  SetTrainMovementValues(8, NextElementPosition, NextEntryPos);
1547  // NextElement is the element to be entered
1548  }
1549  // follow the continuation exits:-
1550  else if((LeadElement > -1) && (Track->TrackElementAt(209, LeadElement).TrackType == Continuation))
1551  {
1553  // Use LeadElement for calcs if lead is a continuation
1554  }
1555  else if((MidElement > -1) && (Track->TrackElementAt(210, MidElement).TrackType == Continuation))
1556  {
1558  // Use MidElement for calcs if mid is a continuation
1559  }
1560  else if((LagElement > -1) && (Track->TrackElementAt(211, LagElement).TrackType == Continuation))
1561  {
1563  // Use LagElement for calcs if lag is a continuation
1564  }
1565  }
1566  // remove route elements if not autosigs - this section moved from below, was under LagElement > -1 condition but needs to cover LagElement == -1
1567  if(AllRoutes->GetRouteTypeAndGraphics(2, LeadElement, LeadEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1568  // Trains may not be in a route
1569  // Since Straddle = LeadMidLag at this point the train is going to move fully off the existing Lag & fully onto existing Lead element during this function
1570  {
1571  // NB if LeadElement == -1 then the above test returns false
1572  int TempH = Track->TrackElementAt(213, LeadElement).HLoc;
1573  int TempV = Track->TrackElementAt(214, LeadElement).VLoc;
1574  int TempELink = Track->TrackElementAt(215, LeadElement).Link[LeadEntryPos];
1575  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1576  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(10, TempH, TempV, SecondPair);
1577  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(143, FirstPair.first).GetFixedPrefDirElementAt(153,
1578  FirstPair.second).GetELink() == TempELink))
1579  {
1580  AllRoutes->RemoveRouteElement(10, TempH, TempV, TempELink);
1581  }
1582  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(144, SecondPair.first).GetFixedPrefDirElementAt(154,
1583  SecondPair.second).GetELink() == TempELink))
1584  {
1585  AllRoutes->RemoveRouteElement(11, TempH, TempV, TempELink);
1586  }
1587  }
1588  if(AllRoutes->GetRouteTypeAndGraphics(3, MidElement, MidEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1589  // Trains may not be in a route
1590  {
1591  int TempH = Track->TrackElementAt(216, MidElement).HLoc;
1592  int TempV = Track->TrackElementAt(217, MidElement).VLoc;
1593  int TempELink = Track->TrackElementAt(218, MidElement).Link[MidEntryPos];
1594  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1595  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(11, TempH, TempV, SecondPair);
1596  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(145, FirstPair.first).GetFixedPrefDirElementAt(155,
1597  FirstPair.second).GetELink() == TempELink))
1598  {
1599  AllRoutes->RemoveRouteElement(12, TempH, TempV, TempELink);
1600  }
1601  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(146, SecondPair.first).GetFixedPrefDirElementAt(156,
1602  SecondPair.second).GetELink() == TempELink))
1603  {
1604  AllRoutes->RemoveRouteElement(13, TempH, TempV, TempELink);
1605  }
1606  }
1607  if(AllRoutes->GetRouteTypeAndGraphics(4, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::NotAutoSigsRoute)
1608  // Trains may not be in a route
1609  {
1610  int TempH = Track->TrackElementAt(219, LagElement).HLoc;
1611  int TempV = Track->TrackElementAt(220, LagElement).VLoc;
1612  int TempELink = Track->TrackElementAt(221, LagElement).Link[LagEntryPos];
1613  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
1614  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(12, TempH, TempV, SecondPair);
1615  if((FirstPair.first > -1) && (AllRoutes->GetFixedRouteAt(147, FirstPair.first).GetFixedPrefDirElementAt(157,
1616  FirstPair.second).GetELink() == TempELink))
1617  {
1618  AllRoutes->RemoveRouteElement(14, TempH, TempV, TempELink);
1619  }
1620  else if((SecondPair.first > -1) && (AllRoutes->GetFixedRouteAt(148, SecondPair.first).GetFixedPrefDirElementAt(158,
1621  SecondPair.second).GetELink() == TempELink))
1622  {
1623  AllRoutes->RemoveRouteElement(15, TempH, TempV, TempELink);
1624  }
1625  AllRoutes->CheckMapAndRoutes(8); // test
1626  }
1627  if(LagElement > -1)
1628  // not entering at a continuation so can deal with train leaving the lag element
1629  {
1631  // amended below so route elements removed for the complete train (for NotAutoSigsRoutes), so train never standing on a route once it
1632  // starts moving, covers for eliminating route when train reaches buffers, and prevents odd route segments when route extended while
1633  // straddling 3 elements (formerly the last segment was replotted as a route & stayed plotted
1634 
1635  TPrefDirElement PrefDirElement;
1636  // plot locked route marker for any element if appropriate (i.e. if a locked AutoSigs route) but only when train leaves element completely
1637  // as this is a 16x16 graphic
1639  {
1641  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1642  }
1643  if(ContinuationExit(2, LagElement, LagExitPos)) // true if Element is a continuation and Exitpos is the continuation end
1644  {
1645  int RouteNumber;
1646  TrainGone = true;
1647  // flag to indicate train to be deleted - outside this function
1649  {
1650  TTrainController::TContinuationAutoSigEntry ContinuationAutoSigEntry;
1651  ContinuationAutoSigEntry.RouteNumber = RouteNumber;
1652  // calc distance from & inc last signal to exit
1653  int LastElement = LagElement, LastExitPos = LagExitPos, CumDistance = 0;
1654  int NewLastElement = 0, NewLastExitPos = 0;
1655  // need above because can't change LastElement & LastExitPos until both new values obtained
1656  // while((Track->TrackElementAt(684, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200)) as was
1657  while((Track->TrackElementAt(913, LastElement).Config[LastExitPos] != Signal) && (CumDistance < 1200) && (Track->TrackElementAt(897,
1658  LastElement).TrackType != Points))
1659  // extra condition above added because of Moric1998's error (see email of 24/03/2016), where had an autosigs route across points, and another continuation on track not occupied by route so
1660  // failed when found a new element = -1 when tried to cross the continuation. Note this routine can only deal with non points as it uses GetNonPointsOppositeLinkPos
1661  // leave CumDistance as it was in these circumstances.
1662  {
1663  if(LastExitPos < 2)
1664  {
1665  CumDistance += Track->TrackElementAt(685, LastElement).Length01;
1666  }
1667  else
1668  {
1669  CumDistance += Track->TrackElementAt(686, LastElement).Length23;
1670  }
1671  NewLastElement = Track->TrackElementAt(687, LastElement).Conn[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1672  if(NewLastElement == -1)
1673  // this will catch buffers or any other connection failure
1674  {
1675  throw Exception("Error, Connection = -1 in Continuation loop in UpdateTrain");
1676  }
1677  NewLastExitPos = Track->TrackElementAt(688, LastElement).ConnLinkPos[Track->GetNonPointsOppositeLinkPos(LastExitPos)];
1678  if(NewLastExitPos == -1)
1679  {
1680  throw Exception("Error, ConnLinkPos = -1 in Continuation loop in UpdateTrain");
1681  }
1682  LastElement = NewLastElement;
1683  LastExitPos = NewLastExitPos;
1684  }
1685  // if at signal add this in too
1686  if(CumDistance < 1200)
1687  {
1688  CumDistance += Track->TrackElementAt(689, LastElement).Length01; // only need 01 for signal
1689  }
1690  // now have distance including the signal, if >=1200m use 100m (for a signal immediately after the continuation)
1691  // else use 1200m - CumDistance
1692  int FirstDistance = 0;
1693  if(CumDistance >= 1200)
1694  {
1695  FirstDistance = 100;
1696  }
1697  else
1698  {
1699  FirstDistance = 1200 - CumDistance;
1700  }
1701  if(FirstDistance < 100)
1702  {
1703  FirstDistance = 100; // don't allow < 100
1704  }
1705  // can now calc the time delays in seconds - FirstDelay, SecondDelay & ThirdDelay, these are doubles
1706  // BUT - first check whether ExitSpeedFull is very low (Mark had divide by zero error with zero exit speed using v2.4.0)
1707  if(ExitSpeedFull > 20.0)
1708  {
1709  ContinuationAutoSigEntry.FirstDelay = 3.6 * double(FirstDistance) / ExitSpeedFull;
1710  // speed in km/h & distance in m so mult by 3.6 to bring to secs
1711  ContinuationAutoSigEntry.SecondDelay = ContinuationAutoSigEntry.FirstDelay + 4320.0 / ExitSpeedFull;
1712  // 4320.0 = 3.6 * 1200, .0 to make it a double
1713  ContinuationAutoSigEntry.ThirdDelay = ContinuationAutoSigEntry.SecondDelay + 4320.0 / ExitSpeedFull;
1714  }
1715  else
1716  {
1717  ContinuationAutoSigEntry.FirstDelay = 60.0; // 60 secs
1718  ContinuationAutoSigEntry.SecondDelay = 120.0;
1719  ContinuationAutoSigEntry.ThirdDelay = 180.0;
1720  }
1721  ContinuationAutoSigEntry.AccessNumber = 0;
1722  ContinuationAutoSigEntry.PassoutTime = TrainController->TTClockTime;
1724  {
1726  for(VectorIT = TrainController->ContinuationAutoSigVector.begin(); VectorIT != TrainController->ContinuationAutoSigVector.end();
1727  VectorIT++)
1728  {
1729  if(VectorIT->RouteNumber == RouteNumber)
1730  {
1731  // another train has passed out of same route so erase earlier entry
1732  TrainController->ContinuationAutoSigVector.erase(VectorIT);
1733  break;
1734  }
1735  }
1736  }
1737  TrainController->ContinuationAutoSigVector.push_back(ContinuationAutoSigEntry);
1738  }
1740  // need to plot this as returning early so will miss the later plot (not a bridge so don't need PlotAlternativeTrackRouteGraphic)
1741  Display->Update();
1742  // need to keep this since Update() not called for PlotSmallOutput as too slow
1743  Utilities->CallLogPop(659);
1744  return;
1745  }
1746  // above covers for exiting at continuation, need XLinkPos check to exclude entering at a continuation
1747  if(LeadElement > -1)
1748  {
1750  // changed to lead so reset early
1751  {
1752  Track->TrackElementAt(225, LeadElement).Attribute = 0; // red
1754  // don't plot if zoomed out
1755  if(!Display->ZoomOutFlag)
1756  {
1758  }
1759  // covers signal resetting in same direction
1760  }
1761  }
1763  {
1764  if(AllRoutes->GetRouteTypeAndGraphics(5, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1765  {
1766  Display->PlotOutput(23, Track->TrackElementAt(227, LagElement).HLoc * 16, Track->TrackElementAt(228, LagElement).VLoc * 16, EXGraphicPtr);
1767  Display->PlotOutput(24, Track->TrackElementAt(229, LagElement).HLoc * 16, Track->TrackElementAt(230, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1768  TPrefDirElement PrefDirElement;
1769  // plot locked route marker for same side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic
1771  {
1773  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1774  }
1776  LockedVectorNumber)))
1777  {
1779  }
1780  }
1781  }
1782  else if((LeadElement > -1) && (Track->TrackElementAt(233, LeadElement).TrackType == SignalPost))
1783  {
1784  Track->TrackElementAt(234, LeadElement).Attribute = 0; // red
1786  // don't plot if zoomed out
1787  if(!Display->ZoomOutFlag)
1788  {
1790  }
1791  // covers signal passed in opposite direction - replot as red, regardless of what it was before, though should already have been red
1792  }
1794  {
1795  if(AllRoutes->GetRouteTypeAndGraphics(6, LagElement, LagEntryPos, EXGraphicPtr, EntryDirectionGraphicPtr) == TAllRoutes::AutoSigsRoute)
1796  {
1797  Display->PlotOutput(26, Track->TrackElementAt(236, LagElement).HLoc * 16, Track->TrackElementAt(237, LagElement).VLoc * 16, EXGraphicPtr);
1798  Display->PlotOutput(27, Track->TrackElementAt(238, LagElement).HLoc * 16, Track->TrackElementAt(239, LagElement).VLoc * 16, EntryDirectionGraphicPtr);
1799  // below added at v1.3.0 to reset signals if back out of an autosigs route under signaller control after changing direction, when new LeadElement not on route (if it had
1800  // been the route would have been ForceCancelled). Note that the signal is not facing the direction of travel else would have entered
1801  // "if(Track->TrackElementAt(, LagElement).Config[LagExitPos] == Signal)" above and wouldn't be here
1802  int RouteNumber;
1804  // already know it's an autosigsroute, this is just to get the RouteNumber
1805  // addition below at v1.3.2 - found that a signal that had reached double yellow in ContinuationAutoSigs was reset to red when a following train's lag element
1806  // moved off a signal in the normal course of events. It was caused when a train backed out of an autosigs route under signaller control after changing
1807  // direction (see DevHistory.txt). Hence check that the train is in signaller mode and that the train's lead element isn't on the same route before calling SetRouteSignals.
1808  int RouteNumber2;
1810  // already know it's an autosigsroute, this is just to get the RouteNumber
1811  if((TrainMode == Signaller) && (RouteNumber2 != RouteNumber))
1812  // note that if not in a route (as likely) then RouteNumber2 set to -1 )
1813  {
1814  AllRoutes->GetFixedRouteAt(217, RouteNumber).SetRouteSignals(10);
1815  // this was in the 1.3.0 addition but without the condition
1816  }
1817  // end of 1.3.2 addition
1818  // end of 1.3.0.addition
1819  }
1820  TPrefDirElement PrefDirElement;
1821  // plot locked route marker for opp side signal if appropriate (may be covered above but leave in), but only when train leaves element completely as this is a 16x16 graphic (OK - Straddle == LeadMidLag)
1823  {
1825  RailGraphics->LockedRouteCancelPtr[PrefDirElement.GetELink()]);
1826  }
1827  }
1828  }
1829  }
1830  // straddle ONLY changed here, check if 'LeadMid' first & if so ready for updating Elements
1831  if(Straddle == LeadMid)
1832  {
1833  AllowedToPassRedSignal = false;
1834  // if had been allowed to pass then at this point it will move half onto signal so can be reset
1835  // if(LagElement > -1) ResetTrainElementID(LagElement, LagEntryPos);//train fully off old LagElement so can clear TrainOnElement flags - no, reset at earlier call when lag moves off element
1836  if(DerailPending)
1837  // set during last GetLeadElement, but only act on it when train fully on offending point
1838  // i.e. next time Straddle reaches LeadMid
1839  {
1840  Derailed = true;
1841  DerailPending = false;
1845  Utilities->CallLogPop(657);
1846  return;
1847  }
1854  Straddle = MidLag;
1855  // train now fully on the updated Lag & Mid, the front segment is going to move onto the new
1856  // LeadElement during this function (note that if stopped at signal then won't get this far)
1857  if(LeadElement > -1)
1858  {
1860  // i.e an exit continuation only
1861  // if don't exclude entry continuations then can't progress past it
1862  {
1863  LeadElement = -1;
1864  }
1865  else
1866  {
1867  GetLeadElement(0);
1868  // sets or resets DerailPending & StoppedAtSignal, and sets LeadElement values
1870  if(Stopped())
1871  {
1872  if(TrainFailurePending) // ok, stopped so PlotElements set
1873  {
1874  TrainHasFailed(6);
1875  }
1876  Utilities->CallLogPop(658);
1877  return; // i.e. don't move forward one step if next element is a red signal
1878  }
1879  }
1880  }
1881  }
1882  if(LagElement > -1)
1883  {
1884  // below are the actions required at both half moves for LagElement > -1
1886 
1887  // if was in locked route but has timed out when train leaves then plot the normal track graphic over the route graphic that is
1888  // still in BackgroundGraphic[3], if wasn't in a route then will just replot the same BackgroundGraphic
1889  // need to do this for each half element
1890 
1891  TPrefDirElement PrefDirElement;
1892  if(!(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(7, LagElement, LagExitPos, PrefDirElement, LockedVectorNumber)))
1893  {
1894  int RouteNumber; // holder for call below - not used
1896  {
1897  if(Utilities->clTransparent == TColor(0xFFFFFF))
1898  // change to black for a white background
1899  {
1901  // only applies for AutoSigs Route in case was locked & timed out
1902  }
1903  else
1904  // change to white for a dark background
1905  {
1907  // only applies for AutoSigs Route in case was locked & timed out
1908  }
1910  }
1911  }
1913  // above in case train just moving off a bridge & either alternative track in a route - need to keep its route colour,
1914  // or a train on the opposite track - needs to be replotted
1915  }
1916  // update all array values
1917  HOffset[3] = HOffset[2];
1918  HOffset[2] = HOffset[1];
1919  HOffset[1] = HOffset[0];
1920  VOffset[3] = VOffset[2];
1921  VOffset[2] = VOffset[1];
1922  VOffset[1] = VOffset[0];
1923  Graphics::TBitmap *TempPtr = BackgroundPtr[3];
1924 
1925  BackgroundPtr[3] = BackgroundPtr[2];
1926  BackgroundPtr[2] = BackgroundPtr[1];
1927  BackgroundPtr[1] = BackgroundPtr[0];
1928  BackgroundPtr[0] = TempPtr;
1929 
1930  // update headcode graphics depending on Lead entry value
1931  if(LeadElement > -1) // if Lead is -1 then stays as is
1932  {
1934  {
1935  for(int x = 0; x < 4; x++)
1936  {
1937  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
1938  }
1939  }
1940  else
1941  {
1942  for(int x = 0; x < 4; x++)
1943  {
1945  }
1946  }
1947  }
1948  if(TrainMode == Timetable)
1949  {
1951  }
1952  else
1953  {
1955  }
1957 
1958  // plot new seg [0] on Lead & [2] on Mid ([2] always on Mid)
1959  if(LeadElement > -1)
1960  {
1961  if(Straddle == MidLag)
1962  // just about to move half onto the new lead element
1963  {
1965  // pick up new background bitmap [0]
1967  int LeadElementTrainID = Track->TrackElementAt(244, LeadElement).TrainIDOnElement;
1968  if((LeadElementTrainID > -1) && (LeadElementTrainID != TrainID))
1969  // check if own ID for entry at continuation, else crashes into itself!
1970  {
1971  // OK if crossing on a bridge
1972  int OtherTrainEntryPos = TrainController->EntryPos(0, LeadElementTrainID, LeadElement);
1973  if(OtherTrainEntryPos == -1)
1974  {
1975  throw Exception("Error - OtherTrainEntryPos not set");
1976  }
1977  if((Track->TrackElementAt(246, LeadElement).TrackType != Bridge) || (LeadEntryPos == OtherTrainEntryPos) ||
1978  // LeadEntryPos for rear end crashes
1979  (LeadExitPos == OtherTrainEntryPos))
1980  // LeadExitPos for head-on crashes
1981  {
1983  Crashed = true; // only set if Straddle = MidLag
1984  CallingOnFlag = false;
1985  // in case was set, need to disable call on if call on button had been pressed
1986  }
1987  }
1988  else if(MidElement > -1) // will be -1 for continuation entries
1989  {
1990  // check if about to move onto a crossing diagonal that is occupied by another train, and if so crash
1991  int MidExitLinkNum = Track->TrackElementAt(889, MidElement).Link[MidExitPos];
1992  int MidHLoc = Track->TrackElementAt(890, MidElement).HLoc;
1993  int MidVLoc = Track->TrackElementAt(891, MidElement).VLoc;
1994  int OtherTrainID = -1;
1995  if((MidExitLinkNum == 1) || (MidExitLinkNum == 3) || (MidExitLinkNum == 7) || (MidExitLinkNum == 9))
1996  {
1997  if(Track->DiagonalFouledByTrain(0, MidHLoc, MidVLoc, MidExitLinkNum, OtherTrainID))
1998  {
1999  TrainCrashedInto = OtherTrainID;
2000  Crashed = true; // only set if Straddle = MidLag
2001  CallingOnFlag = false;
2002  // in case was set, need to disable call on if call on button had been pressed
2003  }
2004  }
2005  }
2006  }
2007  else
2008  {
2010  // pick up new background bitmap [0]
2012  }
2013  PlotElement[0] = LeadElement;
2015  PlotTrainGraphic(12, 0, Display);
2016  }
2017  if(MidElement > -1)
2018  {
2019  PlotElement[2] = MidElement;
2021  PlotTrainGraphic(1, 2, Display);
2022  }
2023  // plot the new positions for [1] & [3] graphics - [1] on Mid if Straddle = MidLag, on Lead if Straddle = LeadMidLag
2024  // [3] on Lag if Straddle = MidLag, on Mid if Straddle = LeadMidLag
2025  if(Straddle == MidLag)
2026  {
2027  if(MidElement > -1)
2028  {
2029  PlotElement[1] = MidElement;
2031  PlotTrainGraphic(2, 1, Display);
2032  }
2033  if(LagElement > -1)
2034  {
2035  PlotElement[3] = LagElement;
2037  PlotTrainGraphic(3, 3, Display);
2038  }
2039  }
2040  else // Straddle == LeadMidLag
2041  {
2042  if(LeadElement > -1)
2043  {
2044  PlotElement[1] = LeadElement;
2046  PlotTrainGraphic(4, 1, Display);
2047  }
2048  if(MidElement > -1)
2049  {
2050  PlotElement[3] = MidElement;
2052  PlotTrainGraphic(5, 3, Display);
2053  }
2054  }
2055  if(Crashed)
2056  // only reach here if crash into another train, if crash into buffers or an LC then return earlier at the if(Stopped()) test
2057  {
2062  // in case was set, need to disable call on if call on button had been pressed
2069  Straddle = LeadMidLag;
2070  // was MidLag but plotted as LeadMidLag so change Straddle accordingly
2071  Display->Update();
2072  // resurrected when Update() dropped from PlotOutput etc
2073  Utilities->CallLogPop(660);
2074  return;
2075  }
2076  // deal here with station stops & pass times after all replotting done but before Straddle changed
2077  if(TrainMode == Timetable)
2078  {
2079  if(Straddle == LeadMidLag)
2080  {
2081  if((LeadElement > -1) && (MidElement > -1) && !TimetableFinished)
2082  {
2083  // NameInTimetableBeforeCDT returns the number by which the train ActionVectorEntryPtr needs to be incremented
2084  // to point to the location arrival entry - before a change of direction
2085  AnsiString LocName = Track->TrackElementAt(249, LeadElement).ActiveTrackElementName;
2086  bool StopRequired = false;
2087  int TTVPos = NameInTimetableBeforeCDT(1, LocName, StopRequired);
2088  if(TTVPos > -1) // -1 if can't find it or if name is ""
2089  {
2090  // check if at buffers (no, dropped buffer check to allow to crash into buffers) or a through station stop,
2091  // or a station where next element contains a train or a stop signal, if so
2092  // stop now, note that for 2nd check, if next element is a bridge then will have stopped by now so no need
2093  // to test the actual track the train is on since it can't be a platform
2094  TTrackElement LeadTrackElement = Track->TrackElementAt(258, LeadElement);
2095  TTrackElement NextTrackElement; // default for now
2096  bool TrainAtStopLinkPos1 = (LeadTrackElement.StationEntryStopLinkPos1 == LeadEntryPos);
2097  bool TrainAtStopLinkPos2 = (LeadTrackElement.StationEntryStopLinkPos2 == LeadEntryPos);
2098  bool ForwardConnection = (LeadTrackElement.Conn[LeadExitPos] > -1);
2099  int NextElementEntryPos = -1;
2100  int NextElementExitPos = -1;
2101  bool TrainOnNextElement = false;
2102  bool StopSignalAtNextElement = false;
2103  if(ForwardConnection)
2104  // if no forward connection can't derive anything from it without errors
2105  {
2106  NextTrackElement = Track->TrackElementAt(262, LeadTrackElement.Conn[LeadExitPos]);
2107  NextElementEntryPos = LeadTrackElement.ConnLinkPos[LeadExitPos];
2108  NextElementExitPos = Track->GetNonPointsOppositeLinkPos(NextElementEntryPos);
2109  // this is only for signals so no need to worry about points ambiguity
2110  TrainOnNextElement = (NextTrackElement.TrainIDOnElement > -1);
2111  StopSignalAtNextElement = ((NextTrackElement.Config[NextElementExitPos] == Signal) && (NextTrackElement.Attribute == 0));
2112  }
2113  if(TrainAtStopLinkPos1 || TrainAtStopLinkPos2 || (ForwardConnection && (TrainOnNextElement || StopSignalAtNextElement)))
2114  {
2115  if(TTVPos > 0)
2116  {
2118  ActionVectorEntryPtr += TTVPos;
2119  }
2120  if(StopRequired)
2121  {
2122  StoppedAtLocation = true;
2123  StoppedAtSignal = false;
2124  // may have been set earlier at line 925 so need to reset as
2125  // StoppedAtLocation takes precedence and don't want both set at same time or have flashing graphic
2126  // in zoom out mode
2127  if(!TrainFailed)
2128  {
2130  // pale green
2131  }
2134  {
2135  TimeTimeLocArrived = true;
2136  // used in case of later signaller control, when need to know
2137  // whether had arrived or not, to avoid sending the arrival
2138  // message twice, see TInterface::TimetableControl1Click
2139  }
2140  }
2141  else
2142  {
2144  }
2146  {
2148  }
2149  // don't alter ActionVectorEntryPtr if at a TimeTimeLoc (& can't be anything else other than TimeLoc or PassTime after calling NameInTimetableBeforeCDT successfully)
2151  }
2152  }
2153  }
2154  }
2155  }
2156  if(Straddle == MidLag)
2157  {
2158  Straddle = LeadMidLag;
2159  FirstHalfMove = false;
2160  }
2161  else if(Straddle == LeadMidLag)
2162  {
2163  Straddle = LeadMid;
2164  FirstHalfMove = true;
2165  }
2166  else if(Straddle == LeadMid)
2167  {
2168  throw Exception("Error, Straddle shouldn't be LeadMid prior to resetting at exit from UpdateTrain");
2169  }
2170  if(TrainFailurePending) // ok, moving but PlotElements set above
2171  {
2172  TrainHasFailed(7);
2173  }
2174  Display->Update();
2175  // need to keep this since Update() not called for PlotSmallOutput as too slow
2176  Utilities->CallLogPop(661);
2177 }
2178 
2179 // ----------------------------------------------------------------------------
2180 
2181 Graphics::TBitmap *TTrain::SetOneGraphicCode(char CodeChar)
2182 {
2183  switch(CodeChar)
2184  {
2185  case '0':
2186  return(RailGraphics->Code0);
2187 
2188  case '1':
2189  return(RailGraphics->Code1);
2190 
2191  case '2':
2192  return(RailGraphics->Code2);
2193 
2194  case '3':
2195  return(RailGraphics->Code3);
2196 
2197  case '4':
2198  return(RailGraphics->Code4);
2199 
2200  case '5':
2201  return(RailGraphics->Code5);
2202 
2203  case '6':
2204  return(RailGraphics->Code6);
2205 
2206  case '7':
2207  return(RailGraphics->Code7);
2208 
2209  case '8':
2210  return(RailGraphics->Code8);
2211 
2212  case '9':
2213  return(RailGraphics->Code9);
2214 
2215  case 'A':
2216  return(RailGraphics->CodeA);
2217 
2218  case 'B':
2219  return(RailGraphics->CodeB);
2220 
2221  case 'C':
2222  return(RailGraphics->CodeC);
2223 
2224  case 'D':
2225  return(RailGraphics->CodeD);
2226 
2227  case 'E':
2228  return(RailGraphics->CodeE);
2229 
2230  case 'F':
2231  return(RailGraphics->CodeF);
2232 
2233  case 'G':
2234  return(RailGraphics->CodeG);
2235 
2236  case 'H':
2237  return(RailGraphics->CodeH);
2238 
2239  case 'I':
2240  return(RailGraphics->CodeI);
2241 
2242  case 'J':
2243  return(RailGraphics->CodeJ);
2244 
2245  case 'K':
2246  return(RailGraphics->CodeK);
2247 
2248  case 'L':
2249  return(RailGraphics->CodeL);
2250 
2251  case 'M':
2252  return(RailGraphics->CodeM);
2253 
2254  case 'N':
2255  return(RailGraphics->CodeN);
2256 
2257  case 'O':
2258  return(RailGraphics->CodeO);
2259 
2260  case 'P':
2261  return(RailGraphics->CodeP);
2262 
2263  case 'Q':
2264  return(RailGraphics->CodeQ);
2265 
2266  case 'R':
2267  return(RailGraphics->CodeR);
2268 
2269  case 'S':
2270  return(RailGraphics->CodeS);
2271 
2272  case 'T':
2273  return(RailGraphics->CodeT);
2274 
2275  case 'U':
2276  return(RailGraphics->CodeU);
2277 
2278  case 'V':
2279  return(RailGraphics->CodeV);
2280 
2281  case 'W':
2282  return(RailGraphics->CodeW);
2283 
2284  case 'X':
2285  return(RailGraphics->CodeX);
2286 
2287  case 'Y':
2288  return(RailGraphics->CodeY);
2289 
2290  case 'Z':
2291  return(RailGraphics->CodeZ);
2292 
2293  case 'a':
2294  return(RailGraphics->Code_a);
2295 
2296  case 'b':
2297  return(RailGraphics->Code_b);
2298 
2299  case 'c':
2300  return(RailGraphics->Code_c);
2301 
2302  case 'd':
2303  return(RailGraphics->Code_d);
2304 
2305  case 'e':
2306  return(RailGraphics->Code_e);
2307 
2308  case 'f':
2309  return(RailGraphics->Code_f);
2310 
2311  case 'g':
2312  return(RailGraphics->Code_g);
2313 
2314  case 'h':
2315  return(RailGraphics->Code_h);
2316 
2317  case 'i':
2318  return(RailGraphics->Code_i);
2319 
2320  case 'j':
2321  return(RailGraphics->Code_j);
2322 
2323  case 'k':
2324  return(RailGraphics->Code_k);
2325 
2326  case 'l':
2327  return(RailGraphics->Code_l);
2328 
2329  case 'm':
2330  return(RailGraphics->Code_m);
2331 
2332  case 'n':
2333  return(RailGraphics->Code_n);
2334 
2335  case 'o':
2336  return(RailGraphics->Code_o);
2337 
2338  case 'p':
2339  return(RailGraphics->Code_p);
2340 
2341  case 'q':
2342  return(RailGraphics->Code_q);
2343 
2344  case 'r':
2345  return(RailGraphics->Code_r);
2346 
2347  case 's':
2348  return(RailGraphics->Code_s);
2349 
2350  case 't':
2351  return(RailGraphics->Code_t);
2352 
2353  case 'u':
2354  return(RailGraphics->Code_u);
2355 
2356  case 'v':
2357  return(RailGraphics->Code_v);
2358 
2359  case 'w':
2360  return(RailGraphics->Code_w);
2361 
2362  case 'x':
2363  return(RailGraphics->Code_x);
2364 
2365  case 'y':
2366  return(RailGraphics->Code_y);
2367 
2368  case 'z':
2369  return(RailGraphics->Code_z);
2370 
2371  default:
2372  return(RailGraphics->TempHeadCode);
2373  }
2374 }
2375 
2376 // ----------------------------------------------------------------------------
2377 
2378 void TTrain::SetHeadCodeGraphics(int Caller, AnsiString Code)
2379 {
2380  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetHeadCodeGraphics," + HeadCode);
2381  if(Code.Length() != 4)
2382  {
2383  TrainController->StopTTClockMessage(62, "Headcode Incorrect length");
2384  }
2385  for(int x = 1; x < 5; x++) // AnsiString indices start at 1
2386  {
2387  HeadCodeGrPtr[x - 1]->Assign(SetOneGraphicCode(Code[x]));
2388  }
2389  if(BackgroundColour != clB5G5R5)
2390  // i.e. not the basic graphic colour as loaded from resource file
2391  {
2392  for(int x = 0; x < 4; x++)
2393  {
2395  }
2396  }
2397  Utilities->CallLogPop(1484);
2398 }
2399 
2400 // ----------------------------------------------------------------------------
2401 
2402 void TTrain::GetLeadElement(int Caller)
2403 // assumes Mid & Lag already set, sets LeadElement,
2404 // LeadEntryPos, LeadExitPos & DerailPending (don't want to act on it immediately)
2405 {
2406  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetLeadElement," + HeadCode);
2407  DerailPending = false;
2411  {
2412  // attr 0=straight, - links 0 & 1 (0 = lead)
2413  // attr 1=diverging, - links 2 & 3 (2 = lead)
2414  // set appropriate next element or derail - use a subroutine & return element & bool for derail
2415  // points always have links 0 & 2 = lead, link 1 = trailing straight, link 3 = training diverging
2416 
2417  // if enter at lead, exit at whatever attr set at
2418  // if enter at lag, exit at lead, but set derail wrt attribute
2419  if((LeadEntryPos == 0) && (Track->TrackElementAt(272, LeadElement).Attribute == 0))
2420  {
2421  LeadExitPos = 1;
2422  }
2423 
2424  // strictly speaking shouldn't need to set to 0 and 2 correctly since TrackIsInARoute caters for both, but
2425  // best to be on safe side
2426  else if(LeadEntryPos == 0)
2427  {
2428  LeadEntryPos = 2;
2429  LeadExitPos = 3;
2430  }
2431  else if((LeadEntryPos == 2) && (Track->TrackElementAt(273, LeadElement).Attribute == 0))
2432  {
2433  LeadEntryPos = 0;
2434  LeadExitPos = 1;
2435  }
2436  else if(LeadEntryPos == 2)
2437  {
2438  LeadExitPos = 3;
2439  }
2440 
2441  else if((LeadEntryPos == 1) && (Track->TrackElementAt(274, LeadElement).Attribute == 0))
2442  {
2443  LeadExitPos = 0;
2444  }
2445  else if(LeadEntryPos == 1)
2446  {
2447  LeadExitPos = 0;
2448  DerailPending = true;
2449  }
2450  else if((LeadEntryPos == 3) && (Track->TrackElementAt(275, LeadElement).Attribute == 0))
2451  {
2452  LeadExitPos = 0;
2453  DerailPending = true;
2454  }
2455  else if(LeadEntryPos == 3)
2456  {
2457  LeadExitPos = 0;
2458  }
2459  }
2460  else if(LeadEntryPos == 0)
2461  {
2462  LeadExitPos = 1;
2463  }
2464  else if(LeadEntryPos == 1)
2465  {
2466  LeadExitPos = 0;
2467  }
2468  else if(LeadEntryPos == 2)
2469  {
2470  LeadExitPos = 3;
2471  }
2472  else if(LeadEntryPos == 3)
2473  {
2474  LeadExitPos = 2;
2475  }
2476  // TTrackElement TrackElement = Track->TrackElementAt(276, LeadElement);
2477 /* signal check moved to Update() function
2478  if((TrackElement.TrackType == SignalPost) && (TrackElement.Config[LeadExitPos] == Signal)
2479  && (TrackElement.Attribute == 0))//0 = red
2480  {
2481  StoppedAtSignal = true; //comment out for test of locked route graphic replot
2482  }
2483  else
2484  {
2485  StoppedAtSignal = false;
2486  }
2487 */
2488  Utilities->CallLogPop(662);
2489 }
2490 
2491 // ----------------------------------------------------------------------------
2492 
2493 void TTrain::GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
2494 {
2495  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetOffsetValues," + AnsiString(Link) + "," + HeadCode);
2496  switch(Link)
2497  {
2498  case 1:
2499  {
2500  HOffset = 0;
2501  VOffset = 0;
2502  break;
2503  }
2504 
2505  case 2:
2506  {
2507  HOffset = 4;
2508  VOffset = 0;
2509  break;
2510  }
2511 
2512  case 3:
2513  {
2514  HOffset = 8;
2515  VOffset = 0;
2516  break;
2517  }
2518 
2519  case 4:
2520  {
2521  HOffset = 0;
2522  VOffset = 4;
2523  break;
2524  }
2525 
2526  case 6:
2527  {
2528  HOffset = 8;
2529  VOffset = 4;
2530  break;
2531  }
2532 
2533  case 7:
2534  {
2535  HOffset = 0;
2536  VOffset = 8;
2537  break;
2538  }
2539 
2540  case 8:
2541  {
2542  HOffset = 4;
2543  VOffset = 8;
2544  break;
2545  }
2546 
2547  case 9:
2548  {
2549  HOffset = 8;
2550  VOffset = 8;
2551  break;
2552  }
2553 
2554  default:
2555  {
2556  throw Exception("Error in GetOffsetValues - Link value wrong");
2557  }
2558  }
2559  Utilities->CallLogPop(674);
2560 }
2561 
2562 // ---------------------------------------------------------------------------
2563 
2564 bool TTrain::LowEntryValue(int EntryLink) const
2565 {
2566 /* returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i.e.
2567  the character that is red or blue) is the last character of the headcode, otherwise it's the first character of the headcode
2568 */
2569  if((EntryLink == 1) || (EntryLink == 2) || (EntryLink == 4) || (EntryLink == 7))
2570  {
2571  return(true);
2572  }
2573  else
2574  {
2575  return(false);
2576  }
2577 }
2578 
2579 // ---------------------------------------------------------------------------
2580 
2581 void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2582 {
2583  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) + "," +
2584  AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2585  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2586  // default values
2587  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2588 
2589  TAllRoutes::TRouteType RouteType;
2590 
2591  RouteType = AllRoutes->GetRouteTypeAndGraphics(11, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2592 
2593  TRect SourceRect, DestRect;
2594 
2595  DestRect.init(0, 0, 8, 8); // initialise left, top, right, bottom
2596  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2597  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2598  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap;
2599 
2600  TempGraphic->PixelFormat = pf8bit;
2601  TempGraphic->Width = 16;
2602  TempGraphic->Height = 16;
2603  TTrackElement TempElement = Track->TrackElementAt(286, Element);
2604 
2605  if(TempElement.TrackType == Points)
2606  {
2607  TempGraphic->Assign(TempElement.GraphicPtr);
2608  TempGraphic->Transparent = true;
2609  TempGraphic->TransparentColor = Utilities->clTransparent;
2610  if(RouteType == TAllRoutes::AutoSigsRoute)
2611  {
2612  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2613  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2614  }
2615  else
2616  {
2617  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2618  }
2619  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2620  }
2621  else if(TempElement.TrackType == GapJump) // plot set gap
2622  {
2623  if(TempElement.SpeedTag == 88)
2624  {
2625  TempGraphic->Assign(RailGraphics->gl88set);
2626  }
2627  else if(TempElement.SpeedTag == 89)
2628  {
2629  TempGraphic->Assign(RailGraphics->gl89set);
2630  }
2631  else if(TempElement.SpeedTag == 90)
2632  {
2633  TempGraphic->Assign(RailGraphics->gl90set);
2634  }
2635  else if(TempElement.SpeedTag == 91)
2636  {
2637  TempGraphic->Assign(RailGraphics->gl91set);
2638  }
2639  else if(TempElement.SpeedTag == 92)
2640  {
2641  TempGraphic->Assign(RailGraphics->gl92set);
2642  }
2643  else if(TempElement.SpeedTag == 93)
2644  {
2645  TempGraphic->Assign(RailGraphics->bm93set);
2646  }
2647  else if(TempElement.SpeedTag == 94)
2648  {
2649  TempGraphic->Assign(RailGraphics->bm94set);
2650  }
2651  else if(TempElement.SpeedTag == 95)
2652  {
2653  TempGraphic->Assign(RailGraphics->gl95set);
2654  }
2655  TempGraphic->Transparent = true;
2656  TempGraphic->TransparentColor = Utilities->clTransparent;
2657  if(RouteType == TAllRoutes::AutoSigsRoute)
2658  {
2659  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2660  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2661  }
2662  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2663  }
2664  // new for version 0.6
2665  else if(TempElement.TrackType == SignalPost)
2666  {
2667  if(TempElement.SigAspect == TTrackElement::GroundSignal)
2668  {
2669  for(int x = 0; x < 40; x++)
2670  {
2671  if((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2672  // need to stop aspect
2673  {
2674  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2675  break;
2676  }
2677  }
2678  }
2679  else // normal signal
2680  {
2681  TempGraphic->Assign(TempElement.GraphicPtr);
2682  // GraphicPtr set to normal signal in a signal track element
2683  }
2684  TempGraphic->Transparent = true;
2685  TempGraphic->TransparentColor = Utilities->clTransparent;
2686  if(RouteType == TAllRoutes::AutoSigsRoute)
2687  {
2688  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2689  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2690  }
2691  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2692  }
2693  else
2694  {
2695  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2696  // can't name points gaps or signals so 'else' OK
2697  bool FoundFlag;
2698  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2699  if(FoundFlag)
2700  {
2702  {
2703  GraphicPtr->Canvas->CopyRect(DestRect, Track->InactiveTrackElementAt(26, IMPair.first).GraphicPtr->Canvas, SourceRect);
2704  TempGraphic->Assign(RailGraphics->bmName);
2705  TempGraphic->Transparent = true;
2706  TempGraphic->TransparentColor = Utilities->clTransparent;
2707  if(RouteType == TAllRoutes::AutoSigsRoute)
2708  {
2709  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2710  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2711  }
2712  else
2713  {
2714  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2715  }
2716  // draw track on top
2717  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2718  }
2719  else if(Track->InactiveTrackElementAt(116, IMPair.first).TrackType == LevelCrossing)
2720  {
2721  TempGraphic->Assign(TempElement.GraphicPtr);
2722  TempGraphic->Transparent = true;
2723  TempGraphic->TransparentColor = Utilities->clTransparent;
2724  // note that can't be an AutoSigsRoute
2725  // now overlay the LC central portion
2726  int BDVectorPos = -1; //not used
2727  if(Track->AnyLinkedBarrierDownVectorManual(0, Track->InactiveTrackElementAt(130, IMPair.first).HLoc, Track->InactiveTrackElementAt(131, IMPair.first).VLoc, BDVectorPos))
2728  {
2729  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlainMan);
2730  }
2731  else
2732  {
2733  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2734  }
2735  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2736  }
2737  else
2738  {
2739  TempGraphic->Assign(TempElement.GraphicPtr);
2740  TempGraphic->Transparent = true;
2741  TempGraphic->TransparentColor = Utilities->clTransparent;
2742  if(RouteType == TAllRoutes::AutoSigsRoute)
2743  {
2744  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2745  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2746  }
2747  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2748  }
2749  }
2750  else
2751  {
2752  TempGraphic->Assign(TempElement.GraphicPtr);
2753  TempGraphic->Transparent = true;
2754  TempGraphic->TransparentColor = Utilities->clTransparent;
2755  if(RouteType == TAllRoutes::AutoSigsRoute)
2756  {
2757  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2758  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2759  }
2760  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2761  }
2762  }
2763  delete TempGraphic;
2764  Utilities->CallLogPop(675);
2765 }
2766 
2767 // ---------------------------------------------------------------------------
2768 
2769 // This was an attempt to pick up the actual 8x8 graphic from the display, so that text & user graphics would show as soon as the train passed, and overwrite it with the
2770 // reconstructed track, and it works ok but for the little arrows showing route directions at start and end, which extend beyond the track. It doesn't matter for autosig
2771 // routes because they are replotted (alomg with the direction arrows) but for others they shouldn't be. Leave in in case an easy way to remove these pointers comes to mind.
2772 /*
2773 
2774  void TTrain::PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
2775  {
2776  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PickUpBackgroundBitmap," + AnsiString(HOffset) +
2777  "," + AnsiString(VOffset) + "," + AnsiString(Element) + "," + AnsiString(EntryPos) + "," + HeadCode);
2778  TAllRoutes::TRouteType RouteType;
2779  Graphics::TBitmap *EXGraphicPtr = RailGraphics->bmTransparentBgnd;
2780  // default values
2781  Graphics::TBitmap *EntryDirectionGraphicPtr = RailGraphics->bmTransparentBgnd;
2782  TTrackElement TempElement = Track->TrackElementAt(, Element); //this is a copy of the element passed into the function
2783  TRect SourceRect, DestRect, ScreenSourceRect;
2784  RouteType = AllRoutes->GetRouteTypeAndGraphics(7, Element, EntryPos, EXGraphicPtr, EntryDirectionGraphicPtr);
2785 
2786  DestRect.init(0, 0, 8, 8); //initialise left, top, right, bottom
2787  // note right and bottom rect co-ordinates are 1 greater than the pixel area
2788  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
2789 
2790  //add text & user graphics if any to *GraphicPtr prior to adding the track
2791  int Left = ((TempElement.HLoc - Display->DisplayOffsetH) * 16) + HOffset;
2792  int Top = ((TempElement.VLoc - Display->DisplayOffsetV) * 16) + VOffset;
2793  int Right = Left + 8;
2794  int Bottom = Top + 8;
2795  ScreenSourceRect.init(Left, Top, Right, Bottom);
2796  GraphicPtr->Canvas->CopyMode = cmSrcCopy;
2797  GraphicPtr->Canvas->CopyRect(DestRect, Display->GetImage()->Canvas, ScreenSourceRect);
2798 
2799  Graphics::TBitmap *TempGraphic = new Graphics::TBitmap; //this will hold the 16x16 reconstructed element, prior to transfer of the 8x8 bit to *GraphicPtr
2800  TempGraphic->PixelFormat = pf8bit;
2801  TempGraphic->Width = 16;
2802  TempGraphic->Height = 16;
2803 
2804  Graphics::TBitmap *SourceGraphic = new Graphics::TBitmap; //this will hold the 8x8 element segment from TempGraphic - needed because to keep transparency have to use Draw, not CopyRect
2805  SourceGraphic->PixelFormat = pf8bit;
2806  SourceGraphic->Width = 16;
2807  SourceGraphic->Height = 16;
2808  SourceGraphic->Transparent = true;
2809  SourceGraphic->TransparentColor = Utilities->clTransparent;
2810 
2811  if (TempElement.TrackType == Points)
2812  {
2813  TempGraphic->Assign(TempElement.GraphicPtr);
2814  TempGraphic->Transparent = true;
2815  TempGraphic->TransparentColor = Utilities->clTransparent;
2816  if (RouteType == TAllRoutes::AutoSigsRoute)
2817  {
2818  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2819  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2820  }
2821  else
2822  {
2823  TempGraphic->Canvas->Draw(0, 0, Track->GetFilletGraphic(1, TempElement)); // add fillet
2824  }
2825  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2826  }
2827  else if (TempElement.TrackType == GapJump) // plot set gap
2828  {
2829  if (TempElement.SpeedTag == 88)
2830  TempGraphic->Assign(RailGraphics->gl88set);
2831  else if (TempElement.SpeedTag == 89)
2832  TempGraphic->Assign(RailGraphics->gl89set);
2833  else if (TempElement.SpeedTag == 90)
2834  TempGraphic->Assign(RailGraphics->gl90set);
2835  else if (TempElement.SpeedTag == 91)
2836  TempGraphic->Assign(RailGraphics->gl91set);
2837  else if (TempElement.SpeedTag == 92)
2838  TempGraphic->Assign(RailGraphics->gl92set);
2839  else if (TempElement.SpeedTag == 93)
2840  TempGraphic->Assign(RailGraphics->bm93set);
2841  else if (TempElement.SpeedTag == 94)
2842  TempGraphic->Assign(RailGraphics->bm94set);
2843  else if (TempElement.SpeedTag == 95)
2844  TempGraphic->Assign(RailGraphics->gl95set);
2845  TempGraphic->Transparent = true;
2846  TempGraphic->TransparentColor = Utilities->clTransparent;
2847  if (RouteType == TAllRoutes::AutoSigsRoute) {
2848  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2849  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2850  }
2851  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2852  }
2853  // new for version 0.6
2854  else if (TempElement.TrackType == SignalPost)
2855  {
2856  if (TempElement.SigAspect == TTrackElement::GroundSignal)
2857  {
2858  for (int x = 0; x < 40; x++)
2859  {
2860  if ((Track->SigTableGroundSignal[x].SpeedTag == TempElement.SpeedTag) && (Track->SigTableGroundSignal[x].Attribute == 0))
2861  // need to stop aspect
2862  {
2863  TempGraphic->Assign(Track->SigTableGroundSignal[x].SigPtr);
2864  break;
2865  }
2866  }
2867  }
2868  else // normal signal
2869  {
2870  TempGraphic->Assign(TempElement.GraphicPtr);
2871  // GraphicPtr set to normal signal in a signal track element
2872  }
2873  TempGraphic->Transparent = true;
2874  TempGraphic->TransparentColor = Utilities->clTransparent;
2875  if (RouteType == TAllRoutes::AutoSigsRoute) {
2876  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2877  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2878  }
2879  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2880  }
2881  else {
2882  // first check if there's a NamedNonStationLocation element at that position & if so pick up that as the background
2883  // can't name points gaps or signals so 'else' OK
2884  bool FoundFlag;
2885  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap
2886  (4, TempElement.HLoc, TempElement.VLoc, FoundFlag);
2887  if (FoundFlag)
2888  {
2889  if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == NamedNonStationLocation)
2890  {
2891  GraphicPtr->Canvas->CopyRect(DestRect,
2892  Track->InactiveTrackElementAt(, IMPair.first).GraphicPtr->Canvas, SourceRect);
2893  TempGraphic->Assign(RailGraphics->bmName);
2894  TempGraphic->Transparent = true;
2895  TempGraphic->TransparentColor = Utilities->clTransparent;
2896  if (RouteType == TAllRoutes::AutoSigsRoute)
2897  {
2898  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2899  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2900  }
2901  else
2902  TempGraphic->Canvas->Draw(0, 0, TempElement.GraphicPtr);
2903  // draw track on top
2904  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2905  SourceRect);
2906  }
2907  else if (Track->InactiveTrackElementAt(, IMPair.first).TrackType == LevelCrossing) {
2908  TempGraphic->Assign(TempElement.GraphicPtr);
2909  TempGraphic->Transparent = true;
2910  TempGraphic->TransparentColor = Utilities->clTransparent;
2911  // note that can't be an AutoSigsRoute
2912  // now overlay the LC central portion
2913  TempGraphic->Canvas->Draw(0, 0, RailGraphics->LCPlain);
2914  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2915  SourceRect);
2916  }
2917  else {
2918  TempGraphic->Assign(TempElement.GraphicPtr);
2919  TempGraphic->Transparent = true;
2920  TempGraphic->TransparentColor = Utilities->clTransparent;
2921  if (RouteType == TAllRoutes::AutoSigsRoute) {
2922  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2923  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2924  }
2925  GraphicPtr->Canvas->CopyRect(DestRect, TempGraphic->Canvas,
2926  SourceRect);
2927  }
2928  }
2929  else {
2930  TempGraphic->Assign(TempElement.GraphicPtr);
2931  TempGraphic->Transparent = true;
2932  TempGraphic->TransparentColor = Utilities->clTransparent;
2933  if (RouteType == TAllRoutes::AutoSigsRoute) {
2934  TempGraphic->Canvas->Draw(0, 0, EXGraphicPtr);
2935  TempGraphic->Canvas->Draw(0, 0, EntryDirectionGraphicPtr);
2936  }
2937  SourceGraphic->Canvas->CopyRect(DestRect, TempGraphic->Canvas, SourceRect);
2938  GraphicPtr->Canvas->Draw(0, 0, SourceGraphic);
2939  }
2940  }
2941  delete TempGraphic;
2942  delete SourceGraphic;
2943  Utilities->CallLogPop();
2944  }
2945 */
2946 // ---------------------------------------------------------------------------
2947 
2948 void TTrain::PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
2949 {
2950  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainGraphic," + AnsiString(ArrayNumber) + "," + HeadCode);
2951  if(PlotElement[ArrayNumber] == -1)
2952  {
2953  Utilities->CallLogPop(676);
2954  return; // not plotted yet
2955  }
2956  SetTrainElementID(0, PlotElement[ArrayNumber], PlotEntryPos[ArrayNumber]);
2957  // set before plot so gap flashing stops first
2958  Disp->PlotOutput(29, ((Track->TrackElementAt(295, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2959  ((Track->TrackElementAt(296, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), HeadCodePosition[ArrayNumber]);
2960  // Only need to set ID for leading element, stays set until train finally leaves the element
2961  Plotted = true;
2962  Utilities->CallLogPop(677);
2963 }
2964 
2965 // ---------------------------------------------------------------------------
2966 
2967 void TTrain::PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
2968 {
2969  Disp->PlotOutput(30, ((Track->TrackElementAt(297, PlotElement[ArrayNumber]).HLoc * 16) + HOffset[ArrayNumber]),
2970  ((Track->TrackElementAt(298, PlotElement[ArrayNumber]).VLoc * 16) + VOffset[ArrayNumber]), BackgroundPtr[ArrayNumber]);
2971 }
2972 
2973 // ---------------------------------------------------------------------------
2974 
2975 bool TTrain::BufferAtExit(int Caller, int Element, int ExitPos) const
2976 {
2977  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BufferAtExit," + AnsiString(Element) + "," + AnsiString(ExitPos) + "," +
2978  HeadCode);
2979  if((Track->TrackElementAt(299, Element).TrackType == Buffers) && (Track->TrackElementAt(300, Element).Config[ExitPos] == End))
2980  {
2981  Utilities->CallLogPop(678);
2982  return(true);
2983  }
2984  else
2985  {
2986  Utilities->CallLogPop(679);
2987  return(false);
2988  }
2989 }
2990 
2991 // ---------------------------------------------------------------------------
2992 
2993 bool TTrain::ContinuationExit(int Caller, int Element, int ExitPos) const
2994 {
2995  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationExit," + AnsiString(Element) + "," + AnsiString(ExitPos) +
2996  "," + HeadCode);
2997  if((Track->TrackElementAt(301, Element).TrackType == Continuation) && (Track->TrackElementAt(302, Element).Config[ExitPos] == End))
2998  {
2999  Utilities->CallLogPop(680);
3000  return(true);
3001  }
3002  else
3003  {
3004  Utilities->CallLogPop(681);
3005  return(false);
3006  }
3007 }
3008 
3009 // ---------------------------------------------------------------------------
3010 
3011 bool TTrain::IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
3012 // test whether this train on a bridge on trackpos 0 & 1
3013 {
3014  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos01," + AnsiString(TrackVectorPosition) + "," +
3015  HeadCode);
3016  if(Track->TrackElementAt(303, TrackVectorPosition).TrackType != Bridge)
3017  {
3018  Utilities->CallLogPop(682);
3019  return(false);
3020  }
3021  // if(Track->TrackElementAt(304, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3022  if(Track->TrackElementAt(305, TrackVectorPosition).TrainIDOnBridgeTrackPos01 == TrainID)
3023  {
3024  if(Track->TrackElementAt(306, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3025  {
3026  throw Exception("Error, same train on two different bridge tracks");
3027  }
3028  else
3029  {
3030  Utilities->CallLogPop(684);
3031  return(true);
3032  }
3033  }
3034  else
3035  {
3036  Utilities->CallLogPop(685);
3037  return(false);
3038  }
3039 }
3040 
3041 // ---------------------------------------------------------------------------
3042 
3043 bool TTrain::IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
3044 // test whether this train on a bridge on trackpos 2 & 3
3045 {
3046  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainIDOnBridgeTrackPos23," + AnsiString(TrackVectorPosition) + "," +
3047  HeadCode);
3048  if(Track->TrackElementAt(307, TrackVectorPosition).TrackType != Bridge)
3049  {
3050  Utilities->CallLogPop(686);
3051  return(false);
3052  }
3053  // if(Track->TrackElementAt(308, TrackVectorPosition).TrainIDOnElement != TrainID) return false; No, if a bridge could be one of 2 TrainIDs
3054  if(Track->TrackElementAt(309, TrackVectorPosition).TrainIDOnBridgeTrackPos23 == TrainID)
3055  {
3056  // don't carry out check for train on tracks 0 & 1 else will enter an infinite loop if train on both
3057  Utilities->CallLogPop(687);
3058  return(true);
3059  }
3060  else
3061  {
3062  Utilities->CallLogPop(688);
3063  return(false);
3064  }
3065 }
3066 
3067 // ---------------------------------------------------------------------------
3068 
3069 void TTrain::SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3070 {
3071  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3072  AnsiString(EntryPos) + "," + HeadCode);
3073  Track->TrackElementAt(310, TrackVectorPosition).TrainIDOnElement = TrainID;
3074 
3075  // unplot GapFlash graphics if land on flashing gap (this done before train plotted - see PlotTrainGraphic)
3076  if(Track->GapFlashFlag)
3077  {
3079  {
3082  Track->GapFlashFlag = false;
3083  }
3084  }
3085  if(Track->TrackElementAt(311, TrackVectorPosition).TrackType == Bridge)
3086  {
3087  if(EntryPos == -1)
3088  {
3089  throw Exception("Error, TrackVectorPosition set but not EntryPos in SetTrainElementID");
3090  }
3091  if(EntryPos < 2)
3092  {
3093  Track->TrackElementAt(312, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = TrainID;
3094  }
3095  else
3096  {
3097  Track->TrackElementAt(313, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = TrainID;
3098  }
3099  }
3100  Utilities->CallLogPop(690);
3101 }
3102 
3103 // ---------------------------------------------------------------------------
3104 
3105 void TTrain::ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
3106 {
3107  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ResetTrainElementID," + AnsiString(TrackVectorPosition) + "," +
3108  AnsiString(EntryPos) + "," + HeadCode);
3109  if(Track->TrackElementAt(314, TrackVectorPosition).TrackType != Bridge)
3110  {
3111  Track->TrackElementAt(315, TrackVectorPosition).TrainIDOnElement = -1;
3112  }
3113  else
3114  {
3115  if(EntryPos == -1)
3116  {
3117  throw Exception("Error, TrackVectorPosition set but not EntryPos in ResetTrainElementID");
3118  }
3119  if(EntryPos < 2)
3120  {
3121  Track->TrackElementAt(316, TrackVectorPosition).TrainIDOnBridgeTrackPos01 = -1;
3122  }
3123  else
3124  {
3125  Track->TrackElementAt(317, TrackVectorPosition).TrainIDOnBridgeTrackPos23 = -1;
3126  }
3127  if((EntryPos < 2) && (Track->TrackElementAt(318, TrackVectorPosition).TrainIDOnBridgeTrackPos23 > -1))
3128  // i.e. other train on track 2&3
3129  {
3130  Track->TrackElementAt(319, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(320, TrackVectorPosition).TrainIDOnBridgeTrackPos23;
3131  }
3132  else if((EntryPos > 1) && (Track->TrackElementAt(321, TrackVectorPosition).TrainIDOnBridgeTrackPos01 > -1))
3133  // i.e. other train on track 1&2
3134  {
3135  Track->TrackElementAt(322, TrackVectorPosition).TrainIDOnElement = Track->TrackElementAt(323, TrackVectorPosition).TrainIDOnBridgeTrackPos01;
3136  }
3137  else
3138  {
3139  Track->TrackElementAt(324, TrackVectorPosition).TrainIDOnElement = -1;
3140  }
3141  }
3142  Utilities->CallLogPop(691);
3143 }
3144 
3145 // ---------------------------------------------------------------------------
3146 
3147 void TTrain::PlotAlternativeTrackRouteGraphic(int Caller, unsigned int ElementVecNum, int ElementEntryPos, int HOffset, int VOffset, TStraddle StraddleValue)
3148 {
3149  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotAlternativeTrackRouteGraphic," + AnsiString(ElementVecNum) + "," +
3150  AnsiString(ElementEntryPos) + "," + AnsiString(HOffset) + "," + AnsiString(VOffset) + "," + AnsiString(StraddleValue) + "," + HeadCode);
3151  int LockedVectorNumber;
3152 
3153  if(Track->TrackElementAt(325, ElementVecNum).TrackType != Bridge)
3154  // && (Track->TrackElementAt(326, ElementVecNum).TrackType != Crossover))
3155  {
3156  // only applies for a bridge as there can't be (or shouldn't be) 2 routes on an element that isn't a bridge
3157  Utilities->CallLogPop(692);
3158  return;
3159  }
3160  if(AllRoutes->TrackIsInARoute(0, ElementVecNum, (3 - ElementEntryPos)))
3161  // i.e other track is in a marked route
3162  // LinkPos doesn't have to be the entry position for the above check
3163  {
3164  TRect SourceRect, DestRect;
3165  DestRect.init(0, 0, 8, 8); // left, top, right, bottom
3166  // note right and bottom rect co-ordinates are 1 greater than the pixel area
3167  SourceRect.init(HOffset, VOffset, HOffset + 8, VOffset + 8);
3168  // identify the route element for the other track
3169  TAllRoutes::TRouteElementPair RoutePair1, RoutePair2;
3170  RoutePair1 = AllRoutes->GetRouteElementDataFromRoute2MultiMap(13, Track->TrackElementAt(327, ElementVecNum).HLoc,
3171  Track->TrackElementAt(328, ElementVecNum).VLoc, RoutePair2);
3172  int FirstELink, SecondELink = -1;
3173  FirstELink = AllRoutes->GetFixedRouteAt(149, RoutePair1.first).GetFixedPrefDirElementAt(159, RoutePair1.second).GetELink();
3174  // must be at least one
3175  if(RoutePair2.first > -1)
3176  {
3177  SecondELink = AllRoutes->GetFixedRouteAt(150, RoutePair2.first).GetFixedPrefDirElementAt(160, RoutePair2.second).GetELink();
3178  }
3179  TPrefDirElement RouteElement;
3180  // Graphics::TBitmap *RouteGraphic;
3181  if(FirstELink == Track->TrackElementAt(329, ElementVecNum).Link[ElementEntryPos])
3182  // i.e. other track is in RoutePair2
3183  {
3184  if(SecondELink == -1)
3185  {
3186  throw Exception("Error - Second ELink should be set but isn't in PlotAlternativeTrackRouteGraphic [1]");
3187  }
3188  if(SecondELink == Track->TrackElementAt(330, ElementVecNum).Link[ElementEntryPos])
3189  // error if both have same Link number
3190  {
3191  throw Exception("Error - First & Second ELinks have same value in PlotAlternativeTrackRouteGraphic");
3192  }
3193  // RouteGraphic = AllRoutes->GetFixedRouteAt(151, RoutePair2.first).GetFixedPrefDirElementAt(161, RoutePair2.second).GetEXGraphicPtr();
3194  RouteElement = AllRoutes->GetFixedRouteAt(152, RoutePair2.first).GetFixedPrefDirElementAt(162, RoutePair2.second);
3195  }
3196  else // other track is in RoutePair1
3197  {
3198  // RouteGraphic = AllRoutes->GetFixedRouteAt(153, RoutePair1.first).GetFixedPrefDirElementAt(163, RoutePair1.second).GetEXGraphicPtr();
3199  RouteElement = AllRoutes->GetFixedRouteAt(154, RoutePair1.first).GetFixedPrefDirElementAt(164, RoutePair1.second);
3200  }
3201  Graphics::TBitmap *DestGraphic = new Graphics::TBitmap;
3202  DestGraphic->PixelFormat = pf8bit;
3203  DestGraphic->Width = 8;
3204  DestGraphic->Height = 8;
3205  DestGraphic->Transparent = true;
3206  // has to be transparent or will overwrite the track that the train has just left
3207  DestGraphic->TransparentColor = Utilities->clTransparent;
3208  DestGraphic->Canvas->CopyRect(DestRect, RouteElement.GetRouteEXGraphicPtr()->Canvas, SourceRect);
3209  Display->PlotOutput(31, (Track->TrackElementAt(331, ElementVecNum).HLoc * 16) + HOffset,
3210  (Track->TrackElementAt(332, ElementVecNum).VLoc * 16) + VOffset, DestGraphic);
3211  // plot locked route marker for other route if appropriate
3212  TPrefDirElement PrefDirElement; // holder for next call, unused
3213  // plot locked route marker if appropriate, but only when train leaves element completely as this is a 16x16 graphic
3214  if(StraddleValue == LeadMidLag)
3215  {
3217  PrefDirElement, LockedVectorNumber))
3218  {
3219  Display->PlotOutput(32, (Track->TrackElementAt(333, ElementVecNum).HLoc * 16), (Track->TrackElementAt(334, ElementVecNum).VLoc * 16),
3220  RailGraphics->LockedRouteCancelPtr[RouteElement.GetELink()]);
3221  }
3222  }
3223  delete DestGraphic;
3224  }
3225  // but - there may be a train on the other track - if so need to replot it else the section of route overwrites it
3226  // also can only be a bridge or trains either have already or soon will crash
3227  if(Track->TrackElementAt(335, ElementVecNum).TrackType != Bridge)
3228  {
3229  Utilities->CallLogPop(695);
3230  return;
3231  }
3232  if(ElementEntryPos > 1) // other train is on track 01
3233  {
3234  if(Track->TrackElementAt(336, ElementVecNum).TrainIDOnBridgeTrackPos01 > -1)
3235  {
3237  }
3238  }
3239  else // other train is on track 23
3240  {
3241  if(Track->TrackElementAt(338, ElementVecNum).TrainIDOnBridgeTrackPos23 > -1)
3242  {
3244  }
3245  }
3246  Utilities->CallLogPop(696);
3247 }
3248 
3249 // ---------------------------------------------------------------------------
3250 
3251 void TTrain::CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
3252 {
3253  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckAndCancelRouteForWrongEndEntry," + AnsiString(Element) + "," +
3254  AnsiString(EntryPos) + "," + HeadCode);
3255  int RouteNumber;
3256  bool WrongRoute = false;
3257  TPrefDirElement RouteElement;
3259  TAllRoutes::TRoute2MultiMapIterator Route2MultiMapIterator;
3260 
3261  if(AllRoutes->GetRouteTypeAndNumber(11, Element, EntryPos, RouteNumber) == TAllRoutes::NoRoute)
3262  // here if single track element & no route, or double track element with no route at EntryPos but still need to check if on points or a crossover on non-route track,
3263  // and force-erase route if so (bridge OK of course) note that GetRouteTypeAndNumber allows for points having an EntryPos of 0 or 2 & still returns correct values
3264  {
3265  if((Track->TrackElementAt(340, Element).TrackType == Crossover) || (Track->TrackElementAt(341, Element).TrackType == Points))
3266  {
3267  if(AllRoutes->GetRouteTypeAndNumber(12, Element, (3 - EntryPos), RouteNumber) != TAllRoutes::NoRoute)
3268  // (3-EntryPos) guarantees other route (0->3; 1->2; 2->1; 3->0)
3269  {
3270  if(AllRoutes->GetFixedRouteAt(179, RouteNumber).PrefDirSize() > 2)
3271  {
3272  // don't call for stub end routes
3274  }
3275  AllRoutes->GetModifiableRouteAt(13, RouteNumber).ForceCancelRoute(1);
3276  Utilities->CallLogPop(697);
3277  return;
3278  }
3279  }
3280  // also need to check for a route on a crossing diagonal
3281  TTrackElement TrackElement = Track->TrackElementAt(892, Element);
3282  int LinkNumber = TrackElement.Link[EntryPos];
3283  if((LinkNumber == 1) || (LinkNumber == 3) || (LinkNumber == 7) || (LinkNumber == 9))
3284  {
3285  if(AllRoutes->DiagonalFouledByRoute(0, TrackElement.HLoc, TrackElement.VLoc, LinkNumber))
3286  {
3287  // for LinkNumber = 1, potentially fouled diagonals are at H-1, V, Lk 3 & H, V-1, Lk 7
3288  bool LogActionErrorCalled = false;
3289  // to ensure only called once if have 2 routes on the 2 crossed diagonals
3290  if(LinkNumber == 1)
3291  {
3292  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(0, TrackElement.HLoc - 1, TrackElement.VLoc, 3, RouteNumber))
3293  {
3294  if(AllRoutes->GetFixedRouteAt(207, RouteNumber).PrefDirSize() > 2)
3295  {
3296  // don't call for stub end routes
3298  LogActionErrorCalled = true;
3299  }
3300  AllRoutes->GetModifiableRouteAt(20, RouteNumber).ForceCancelRoute(3);
3301  }
3302  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(1, TrackElement.HLoc, TrackElement.VLoc - 1, 7, RouteNumber))
3303  // not else in case have different routes on each diagonal, though shouldn't be possible
3304  {
3305  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(208, RouteNumber).PrefDirSize() > 2)
3306  {
3307  // don't call for stub end routes
3309  }
3310  AllRoutes->GetModifiableRouteAt(21, RouteNumber).ForceCancelRoute(4);
3311  }
3312  }
3313 
3314  // for LinkNumber = 3, potentially fouled diagonals are at H+1, V, Lk 1 & H, V-1 Lk 9
3315  else if(LinkNumber == 3)
3316  {
3317  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(2, TrackElement.HLoc + 1, TrackElement.VLoc, 1, RouteNumber))
3318  {
3319  if(AllRoutes->GetFixedRouteAt(209, RouteNumber).PrefDirSize() > 2)
3320  {
3321  // don't call for stub end routes
3323  LogActionErrorCalled = true;
3324  }
3325  AllRoutes->GetModifiableRouteAt(22, RouteNumber).ForceCancelRoute(5);
3326  }
3327  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(3, TrackElement.HLoc, TrackElement.VLoc - 1, 9, RouteNumber))
3328  // not else in case have different routes on each diagonal, though shouldn't be possible
3329  {
3330  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(210, RouteNumber).PrefDirSize() > 2)
3331  {
3332  // don't call for stub end routes
3334  }
3335  AllRoutes->GetModifiableRouteAt(23, RouteNumber).ForceCancelRoute(6);
3336  }
3337  }
3338 
3339  // for LinkNumber = 7, potentially fouled diagonals are at H-1, V, Lk 9 & H, V+1 Lk 1
3340  else if(LinkNumber == 7)
3341  {
3342  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(4, TrackElement.HLoc - 1, TrackElement.VLoc, 9, RouteNumber))
3343  {
3344  if(AllRoutes->GetFixedRouteAt(211, RouteNumber).PrefDirSize() > 2)
3345  {
3346  // don't call for stub end routes
3348  LogActionErrorCalled = true;
3349  }
3350  AllRoutes->GetModifiableRouteAt(24, RouteNumber).ForceCancelRoute(7);
3351  }
3352  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(5, TrackElement.HLoc, TrackElement.VLoc + 1, 1, RouteNumber))
3353  // not else in case have different routes on each diagonal, though shouldn't be possible
3354  {
3355  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(212, RouteNumber).PrefDirSize() > 2)
3356  {
3357  // don't call for stub end routes
3359  }
3360  AllRoutes->GetModifiableRouteAt(25, RouteNumber).ForceCancelRoute(8);
3361  }
3362  }
3363 
3364  // for LinkNumber = 9, potentially fouled diagonals are at H+1, V, Lk 7 & H, V+1 Lk 3
3365  else if(LinkNumber == 9)
3366  {
3367  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(6, TrackElement.HLoc + 1, TrackElement.VLoc, 7, RouteNumber))
3368  {
3369  if(AllRoutes->GetFixedRouteAt(213, RouteNumber).PrefDirSize() > 2)
3370  {
3371  // don't call for stub end routes
3373  LogActionErrorCalled = true;
3374  }
3375  AllRoutes->GetModifiableRouteAt(26, RouteNumber).ForceCancelRoute(9);
3376  }
3377  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(7, TrackElement.HLoc, TrackElement.VLoc + 1, 3, RouteNumber))
3378  // not else in case have different routes on each diagonal, though shouldn't be possible
3379  {
3380  if(!LogActionErrorCalled && AllRoutes->GetFixedRouteAt(214, RouteNumber).PrefDirSize() > 2)
3381  {
3382  // don't call for stub end routes
3384  }
3385  AllRoutes->GetModifiableRouteAt(27, RouteNumber).ForceCancelRoute(10);
3386  }
3387  }
3388  }
3389  }
3390  Utilities->CallLogPop(698);
3391  return; // no route on other track or no other track
3392  }
3393  // here if there is a route at Element & EntryPos - so there can't be a route on the other track if a 4 track element, unless it's a bridge & that's ok
3394  for(unsigned int x = 0; x < AllRoutes->GetFixedRouteAt(155, RouteNumber).PrefDirSize(); x++)
3395  {
3396  RouteElement = AllRoutes->GetFixedRouteAt(156, RouteNumber).GetFixedPrefDirElementAt(165, x);
3397  bool PointsAtElement = (Track->TrackElementAt(987, Element).TrackType == Points); // new at v2.4.2 for points check - Xeon repoted it 30/05/20. He found that for routes that
3398  if(RouteElement.GetTrackVectorPosition() == (unsigned int)Element) // cross bridges at both levels can have entrypos 0 & other exitpos 2 so if don't have this check can cancel a route wrongly
3399  {
3400  if(RouteElement.GetELinkPos() == EntryPos)
3401  {
3402  Utilities->CallLogPop(699);
3403  return; // right direction
3404  }
3405  else if((RouteElement.GetELinkPos() == 2) && (EntryPos == 0) && PointsAtElement)
3406  {
3407  Utilities->CallLogPop(700);
3408  return; // right direction (points)
3409  }
3410  else if((RouteElement.GetELinkPos() == 0) && (EntryPos == 2) && PointsAtElement)
3411  {
3412  Utilities->CallLogPop(701);
3413  return; // right direction (points)
3414  }
3415  else if(RouteElement.GetXLinkPos() == EntryPos)
3416  {
3417  WrongRoute = true;
3418  break; // wrong direction
3419  }
3420  else if((RouteElement.GetXLinkPos() == 2) && (EntryPos == 0) && PointsAtElement) // ok for bridges
3421  {
3422  WrongRoute = true;
3423  break; // wrong direction
3424  }
3425  else if((RouteElement.GetXLinkPos() == 0) && (EntryPos == 2) && PointsAtElement) // ok for bridges
3426  {
3427  WrongRoute = true;
3428  break; // wrong direction
3429  }
3430  }
3431  }
3432  if(!WrongRoute)
3433  {
3434  throw Exception("Error, Element in route but no route found in CheckAndCancelRouteForWrongEndEntry");
3435  }
3436  if(AllRoutes->GetFixedRouteAt(180, RouteNumber).PrefDirSize() > 2)
3437  {
3438  // don't call for stub end routes
3440  }
3441  AllRoutes->GetModifiableRouteAt(14, RouteNumber).ForceCancelRoute(2);
3442  Utilities->CallLogPop(703);
3443 }
3444 
3445 // ---------------------------------------------------------------------------
3446 
3447 void TTrain::PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
3448 {
3449  if(BackgroundColour == NewBackgroundColour)
3450  {
3451  return; // don't replot if already correct
3452 
3453  }
3454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainWithNewBackgroundColour," + AnsiString(NewBackgroundColour));
3455  bool ColourError = false, ColourError2 = false;
3456 
3457  RailGraphics->ChangeBackgroundColour(1, FrontCodePtr, FrontCodePtr, NewBackgroundColour, BackgroundColour, ColourError);
3458  if(ColourError)
3459  {
3460  ColourError2 = true;
3461  }
3462  for(int x = 0; x < 4; x++)
3463  {
3464  RailGraphics->ChangeBackgroundColour(2, HeadCodeGrPtr[x], HeadCodeGrPtr[x], NewBackgroundColour, BackgroundColour, ColourError);
3465  if(ColourError)
3466  {
3467  ColourError2 = true;
3468  }
3469  }
3470  if(ColourError2)
3471  {
3473  "ERROR: Colour depth insufficient to display train colours properly. Please ensure that the 'safe' (web) palette of 256 colours can be displayed");
3474  }
3475  // NB need a separate 'for' loop since the plot order can be different from the graphic order depending on the direction
3476  // of motion
3477  for(int x = 0; x < 4; x++)
3478  {
3479  PlotTrainGraphic(6, x, Disp);
3480  }
3481  BackgroundColour = NewBackgroundColour;
3482  Display->Update();
3483  // need to keep this since Update() not called for PlotSmallOutput as too slow
3484  Utilities->CallLogPop(704);
3485 }
3486 
3487 // ---------------------------------------------------------------------------
3488 
3489 void TTrain::SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
3490 /*
3491 Note: Within the loop BrakeRate can only increase and MaxExitSpeed can only reduce
3492 
3493 Summary: Called during PlotStartPosition to set initial values, when stopped and need to restart, and during UpdateTrain when Straddle is LeadMidLag,
3494 i.e. just as the front of a train is about to move fully onto an element, where TrackVectorPosition is the element immediately in front
3495 of the element the front of the train is moving fully on to. The function calculates the times and speeds at the next half-element and
3496 full-element moves.
3497 
3498 Detail: TrackVectorPosition & EntryPos correspond to the TrackVector element immediately in front of where the train is at
3499 the end of the current Update(). EntrySpeed is needed but this is a class data member so isn't passed in. Set the
3500 train BrakeRate to zero (for now, likely to be altered later), & check if zero entry speed with another train directly in front & if so
3501 remain stopped. Pick up the half length value and speed limit for the EntryPos track, and set FrontElementLength to the length of the
3502 EntryPos track, then set LimitingSpeed to the minimum of the element speed limit or the train's maximum speed. Check if running past a
3503 red signal and set SPADFlag if so (use 1 for EntrySpeed rather than 0 as this value is a double so could be slightly in excess of 0).
3504 In this case set the brake rate to maximum to stop as soon as possible.
3505 
3506 For no SPAD calculate the distance that will be travelled at the maximum speed at which the train can exit the next element at half
3507 MaxBrakeRate, this is DistanceAtHalfBraking (also calculate DistanceAtThreeQuarterBraking - used for stopping under signaller control).
3508 DistanceAtHalfBraking is used as the limiting forward look from the next element (i.e. following EntryPos)
3509 for computing the actual braking rate. If no more restrictive speed limits or reasons to stop are found within the forward look then the
3510 train can accelerate or stay at its (local) maximum speed for the next element. The maximum speed on exit from the next element is used
3511 for calculating the forward look because it represents the worst case - i.e. assumes that the train accelerates for the next element.
3512 
3513 A loop is now entered where the CumulativeLength is updated and each successive element (if there are any - current element checked
3514 first to see whether buffers or continuation) in turn is examined: first the length of the
3515 current element is added to the cumulative length; then the half length and speedlimit are set for the next element - points are
3516 followed according to their current setting (Attribute), but derailments are ignored as these are dealt with outside this function; checks
3517 are then made to see whether the next element is a red signal (train should stop before it); next element is a buffer (train should stop
3518 at the end of it so the cumulative length has the next element length added); current element is a buffer (train should stop
3519 at the end of the current element so no need to alter the cumulative length); or have reached a named location stop position. For any of
3520 these reasons, or if stopping under signaller control, there is no more looping, instead the braking rate is calculated to bring the train
3521 to a stop over CumulativeLength. For all normal purposes the braking rate will be less than half (light braking), or less than three
3522 quarters if stopping under signaller control (heavy braking). However if signals are reset in front of a train then the train may need
3523 emergency braking (> 90% max brake rate) and a SPAD may result. Similarly if points are chaged in front of a train that divert it into a
3524 siding then again emeregency braking may be necessary and a crash may result.
3525 
3526 If the train is due to stop then the function calculates the half and full times and speeds and returns. However the calculation depends
3527 on the conditions at entry. If the EntrySpeed is lower than MaxHalfSpeed and the EntryPos element is the one
3528 that the train has to stop at the end of, as it might be for example if train had been stopped at a signal and the next element is a
3529 buffer, then the train accelerates for half the element and brakes for the other half.
3530 Now the BrakeRate is calculated (limited to the MaxBrakeRate), but if it is less than a value calculated at an earlier pass round
3531 the loop then it retains its earlier value (may be due to a close speed restriction that requires more braking than a more distant stop
3532 requirement). The MaxExitSpeedAtHalfBraking (maximum speed at which the train can leave the current element and still stop when required
3533 at half the max braking rate) value is also calculated using EntrySpeed and CumulativeLength, but limiting it to the line speed limit or
3534 train MaxRunningSpeed whichever is the lower. If EntrySpeed > MaxExitSpeedAtHalfBraking then braking is required, so the half and full
3535 speed and time values for the current element are calculated using BrakeRate, EntrySpeed and CurrentElementHalfLength. If need to stop
3536 at the end of the current elemecumulativent for other than a red signal (SPADs can occur) then ExitSpeedFull is set to 0. It should be calculated
3537 as 0 anyway for other than a red signal but this makes sure. If EntrySpeed <= MaxExitSpeedAtHalfBraking then can calculate the half and
3538 full speed and time values for acceleration over the current element, but limit ExitSpeedHalf & Full to MaxExitSpeedAtHalfBraking or to
3539 the current element speed limit if necessary. Check whether ExitSpeedHalf <= EntrySpeed (+0.01 since it's a double) and use constant speed
3540 time values for Half & Full if so, but prior to this increase EntrySpeed if necessary to avoid a divide by zero error.
3541 
3542 If the train is not due to stop within the DistanceAtHalfBraking from the next element following EntryPos then the next element (if there
3543 is one) is checked to see if its speed limit is less than the current value of LimitingSpeed (which is the minimum of any earlier element's
3544 speed limit that has been examined within the loop and the train's MaxRunning speed), and if so LimitingSpeed is set down to it. Now
3545 the MaxExitSpeedAtHalfBraking is calculated, limiting it to LimitingSpeed if less, in case need to accelerate in the current element, in
3546 which case the exit speeds need to be limited to MaxExitSpeedAtHalfBraking. If EntrySpeed > LimitingSpeed then calculate the braking rate
3547 to bring the speed down to LimitingSpeed in CumulativeLength, keeping the existing BrakeRate value if lower and keeping it within
3548 MaxBrakeRate.
3549 
3550 Then, providing the current element isn't a buffer or continuation, the 'Current' values are updated from the 'Next' values ready for
3551 the next loop iteration. The loop is broken out of if the current element is a buffer or continuation, the next element is a
3552 continuation, or (CumulativeLength - FrontElementLength) >= DistanceAtHalfBraking.
3553 
3554 Now the final Half and Full values can be set for braking (if BrakeRate > 0.01), or accelerating - limiting the half and full exit speed
3555 values to MaxExitSpeedAtHalfBraking if necessary, and using constant speed time values if the exit speeds aren't much different to
3556 EntrySpeed and EntrySpeed > 0.01 (to avoid a divide by zero error).
3557 
3558 Note that in no circumstances will a train stop when straddling 3 elements, it will always be fully on two elements. This is ensured
3559 by UpdateTrain() which never sets any stop conditions unless the train is fully on 2 elements when that function returns, i.e. entered
3560 when Straddle == LeadMidLag
3561 */
3562 {
3563  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetTrainMovementValues," + AnsiString(TrackVectorPosition) + "," +
3564  AnsiString(EntryPos) + "," + HeadCode);
3565  int EntryHalfLength, CurrentElementHalfLength, NextElementHalfLength, CumulativeLength = 0, CurrentTrackVectorPosition = TrackVectorPosition;
3566  int DistanceAtHalfBraking, DistanceAtThreeQuarterBraking, ExitPos, NextTrackVectorPosition, NextEntryPos;
3567  bool RedSignalFlag = false, BuffersFlag = false, StationFlag = false, BuffersOrContinuationNowFlag = false, ContinuationNextFlag = false,
3568  TrainInFrontInSignallerModeFlag = false;
3569  double LimitingSpeed, FrontElementMaxSpeed, MaxExitSpeedAtHalfBrakingSquared, MaxExitSpeedAtHalfBraking, NextSpeedLimit, TempBrakeRate;
3570  double ExitSpeedHalfSquared, ExitSpeedFullSquared;
3571  bool SignallerStopRequired = false;
3572 
3574  // set high to begin with to avoid divide by zero errors on restart after stops, will be set lower later
3575 
3576  // Member variables: EntrySpeed, ExitSpeedHalf, ExitSpeedFull, MaxExitSpeed, BrakeRate, EntryTime, ExitTimeHalf, ExitTimeFull, FrontElementSpeedLimit, FrontElementLength;
3577 
3578  OneLengthAccelDecel = false;
3579  BrakeRate = 0;
3580 
3581 //find FrontElementLength & FrontElementSpeedLimit (these correspond to TrackVectorPosition input value);
3582  if(CurrentTrackVectorPosition > -1)
3583  {
3584  if(Track->TrackElementAt(855, CurrentTrackVectorPosition).TrackType == Points) // this test & section added at v0.6
3585  {
3586  if((EntryPos == 0) || (EntryPos == 2))
3587  {
3588  if(Track->TrackElementAt(856, CurrentTrackVectorPosition).Attribute == 0)
3589  {
3590  CurrentElementHalfLength = (Track->TrackElementAt(857, CurrentTrackVectorPosition).Length01) / 2;
3591  FrontElementSpeedLimit = Track->TrackElementAt(858, CurrentTrackVectorPosition).SpeedLimit01;
3592  }
3593  else
3594  {
3595  CurrentElementHalfLength = (Track->TrackElementAt(859, CurrentTrackVectorPosition).Length23) / 2;
3596  FrontElementSpeedLimit = Track->TrackElementAt(860, CurrentTrackVectorPosition).SpeedLimit23;
3597  }
3598  }
3599  else if(EntryPos == 1)
3600  {
3601  CurrentElementHalfLength = (Track->TrackElementAt(861, CurrentTrackVectorPosition).Length01) / 2;
3602  FrontElementSpeedLimit = Track->TrackElementAt(862, CurrentTrackVectorPosition).SpeedLimit01;
3603  }
3604  else // == 3
3605  {
3606  CurrentElementHalfLength = (Track->TrackElementAt(863, CurrentTrackVectorPosition).Length23) / 2;
3607  FrontElementSpeedLimit = Track->TrackElementAt(864, CurrentTrackVectorPosition).SpeedLimit23;
3608  }
3609  }
3610  else
3611  {
3612  if(EntryPos > 1)
3613  {
3614  CurrentElementHalfLength = (Track->TrackElementAt(348, CurrentTrackVectorPosition).Length23) / 2;
3615  FrontElementSpeedLimit = Track->TrackElementAt(349, CurrentTrackVectorPosition).SpeedLimit23;
3616  }
3617  else
3618  {
3619  CurrentElementHalfLength = (Track->TrackElementAt(350, CurrentTrackVectorPosition).Length01) / 2;
3620  FrontElementSpeedLimit = Track->TrackElementAt(351, CurrentTrackVectorPosition).SpeedLimit01;
3621  }
3622  }
3623  EntryHalfLength = CurrentElementHalfLength;
3624  FrontElementLength = 2 * CurrentElementHalfLength;
3625  }
3626  else
3627  {
3628  throw Exception("Error - CurrentTrackVectorPosition < 0 in SetTrainMovementValues");
3629  }
3630  if((CurrentElementHalfLength < 0) || (FrontElementSpeedLimit < 0))
3631  {
3632  throw Exception("Error - HalfLength or SpeedLimit < 0 in SetTrainMovementValues");
3633  }
3634  // check if zero entry speed with another train directly in front & if so remain stopped
3635  if(Track->OtherTrainOnTrack(2, CurrentTrackVectorPosition, EntryPos, TrainID) && (EntrySpeed < 1))
3636  {
3637  EntrySpeed = 0;
3638  ExitSpeedHalf = 0;
3639  ExitSpeedFull = 0;
3640  MaxExitSpeed = 0;
3641  BrakeRate = 0;
3642  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3643  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3644  StoppedForTrainInFront = true;
3645  Utilities->CallLogPop(705);
3646  return;
3647  }
3648  // new at v2.4.0 - check for stopped and zero power
3649  if((EntrySpeed < 1) && PowerAtRail < 1)
3650  {
3651  EntrySpeed = 0;
3652  ExitSpeedHalf = 0;
3653  ExitSpeedFull = 0;
3654  MaxExitSpeed = 0;
3655  BrakeRate = 0;
3656  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
3657  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
3658  StoppedWithoutPower = true;
3659  Utilities->CallLogPop(2125);
3660  return;
3661  }
3662 //set LimitingSpeed & FrontElementMaxSpeed (internal values)
3663  if(BeingCalledOn)
3664  {
3665  LimitingSpeed = CallOnMaxSpeed;
3666  }
3667  else
3668  {
3669  LimitingSpeed = MaximumSpeedLimit;
3670  }
3671  if(LimitingSpeed > FrontElementSpeedLimit)
3672  {
3673  LimitingSpeed = FrontElementSpeedLimit;
3674  }
3675  if(LimitingSpeed > MaxRunningSpeed) //MaxRunningSpeed is set in AddTrain depending on timetable or signaller control mode
3676  {
3677  LimitingSpeed = MaxRunningSpeed;
3678  }
3679  FrontElementMaxSpeed = LimitingSpeed;
3680 
3681 /*
3682  for braking the deceleration rate is constant so the following formuli (Newton's Laws) are used:-
3683  (1) V^2/(3.6^2) = U^2/(3.6^2) - 2FS;
3684  (2) V/3.6 = U/3.6 - FT;
3685  (3) S = UT/3.6 - 0.5FT^2
3686  where(V = final speed in kph [km/h/3.6 = m/s], U = initial speed in km/h, F = deceleration rate in m/s/s, S = distance in m & T = time in secs)
3687 
3688  for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3689  (4) V^2/(3.6^2) - U^2/(3.6^2) = A^2T;
3690  (5) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
3691  where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time in secs
3692  It's a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3693 
3694  calc max speed that can attain on exit from next element (as could accelerate over next element) and use that speed to calc
3695  DistanceAtHalfBraking, if use actual speed may miss a stop requirement just outside look-ahead & accelerate, and at next calc
3696  be unable to stop or have hard acceleration followed immediately by hard braking, this speed makes for smoother operation
3697 */
3698 
3699 // check if running past a red signal without permission
3700  if((Track->TrackElementAt(352, CurrentTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(EntryPos)] == Signal) && (Track->TrackElementAt(353,
3701  CurrentTrackVectorPosition).Attribute == 0) && (EntrySpeed > 1) && !AllowedToPassRedSignal)
3702  {
3703  SPADFlag = true; // user has to intervene to reset & restart after spad
3704  }
3705  if(!SPADFlag)
3706  {
3707  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
3708  // to begin with calc the maximum exit speed (assumes accelerating) and then reduce it if necessary
3709  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
3710  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
3711  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
3712  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
3713 
3714  double ExitSpeedAtMaxBraking;
3715  // below introduced at v2.4.0, was ExitSpeedFull = LimitingSpeed; but that allowed very high brake rates when
3716  // took signaller control of a fast failed train with signaller limiting speed 30km/h
3718  {
3719  ExitSpeedAtMaxBraking = 0;
3720  }
3721  else
3722  {
3723  ExitSpeedAtMaxBraking = sqrt((EntrySpeed * EntrySpeed) - 2 * MaxBrakeRate * FrontElementLength);
3724  }
3725  double SpeedToUse;
3726  // use the highest of LimitingSpeed or ExitSpeedAtMaxBraking - added at v2.4.2 because trains entering at a continuation with zero (or very low) speed
3727  // & 2 elements before signal caused ExitSpeedAtMaxBraking & hence DistanceAtHalfBraking and DistanceAtThreeQuarterBraking to be zero, so no restriction was recognised
3728  // for first element & train accelerated at maximum rate, then at 2nd element train couldn't brake in time and overran the signal - notified by Micke via Discord on 02/06/20
3729  if(ExitSpeedAtMaxBraking > LimitingSpeed)
3730  {
3731  SpeedToUse = ExitSpeedAtMaxBraking;
3732  }
3733  else
3734  {
3735  SpeedToUse = LimitingSpeed;
3736  }
3737  if(ExitSpeedFull > SpeedToUse)
3738  {
3739  ExitSpeedFull = SpeedToUse;
3740  }
3741  DistanceAtHalfBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / MaxBrakeRate;
3742  DistanceAtThreeQuarterBraking = ExitSpeedFull * ExitSpeedFull / 3.6 / 3.6 / 1.5 / MaxBrakeRate; // used for signaller stops
3743 
3744  //now enter a do loop to examine each element in turn from the front of the train to calc the cumulative length and to see if a stop is required (flag set if so -
3745  //RedSignalFlag, BuffersFlag, StationFlag, TrainInFrontInSignallerModeFlag, SignallerStopRequired, StepForwardFlag) in which case there are no more loops
3746  // break out of the loop when ((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking ) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) || SignallerStoppingFlag);
3747 
3748  do
3749  {
3750  RedSignalFlag = false;
3751  BuffersFlag = false;
3752  StationFlag = false;
3753  BuffersOrContinuationNowFlag = false;
3754  ContinuationNextFlag = false;
3755  // have to reset this after the above test
3756  // add current element length to CumulativeLength
3757  CumulativeLength += (2 * CurrentElementHalfLength);
3758  if((CumulativeLength >= DistanceAtThreeQuarterBraking) && (TrainMode == Signaller) && SignallerStoppingFlag)
3759  {
3760  SignallerStopRequired = true;
3761  // once set stays set until SignallerStoppingFlag reset, providing !BuffersOrContinuationNowFlag,
3762  // set SignallerStopBrakeRate to stop in CumulativeLength unless already higher (i.e. can only increase)
3763  double TempBR = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
3764  if(SignallerStopBrakeRate < TempBR)
3765  {
3766  SignallerStopBrakeRate = TempBR;
3767  }
3768  }
3769  // first check for stops within the length of the current element, where don't want any more checks & don't want
3770  // to add in any extra to the CumulativeLength. Only applies for buffers & station stops as signals should have been caught
3771  // during the last loop when the NextTrackVectorPosition was the signal.
3772 
3773  // check if current element is a buffer
3774  if(Track->TrackElementAt(374, CurrentTrackVectorPosition).TrackType == Buffers)
3775  {
3776  // no need to add in the length of this element to CumulativeLength as already included
3777  BuffersFlag = true;
3778  }
3779  // check if current element is a station stop
3780  if(TrainMode == Timetable)
3781  {
3782  bool StopRequired = false;
3783  if(!TimetableFinished && (NameInTimetableBeforeCDT(12, Track->TrackElementAt(375, CurrentTrackVectorPosition).ActiveTrackElementName,
3784  StopRequired) > -1) && ((Track->TrackElementAt(376, CurrentTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) ||
3785  (Track->TrackElementAt(377, CurrentTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3786  {
3787  // no need to add in the length of element to CumulativeLength
3788  if(StopRequired)
3789  {
3790  StationFlag = true;
3791  }
3792  }
3793  }
3794  else
3795  {
3796  StationFlag = false;
3797  }
3798  // set NextHalfLength & NextSpeedLimit, but only if current element not buffers or exit continuation - no next element for them
3799  if(((Track->TrackElementAt(354, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(355,
3800  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
3801  {
3802  BuffersOrContinuationNowFlag = true;
3803  }
3804  if(!BuffersOrContinuationNowFlag && !BuffersFlag && !StationFlag) // skip if buffers or station flags already set
3805  {
3806  if(Track->TrackElementAt(356, CurrentTrackVectorPosition).TrackType == Points)
3807  {
3808  if((EntryPos == 0) || (EntryPos == 2))
3809  {
3810  if(Track->TrackElementAt(357, CurrentTrackVectorPosition).Attribute == 0)
3811  {
3812  ExitPos = 1;
3813  }
3814  else
3815  {
3816  ExitPos = 3;
3817  }
3818  }
3819  else
3820  {
3821  ExitPos = 0;
3822  }
3823  }
3824  else
3825  {
3826  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
3827  }
3828  NextTrackVectorPosition = Track->TrackElementAt(358, CurrentTrackVectorPosition).Conn[ExitPos];
3829  NextEntryPos = Track->TrackElementAt(359, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
3830  if(NextTrackVectorPosition > -1)
3831  {
3832  if(Track->TrackElementAt(845, NextTrackVectorPosition).TrackType == Points)
3833  // this test & section added at v0.6
3834  {
3835  if((NextEntryPos == 0) || (NextEntryPos == 2))
3836  {
3837  if(Track->TrackElementAt(846, NextTrackVectorPosition).Attribute == 0)
3838  {
3839  NextElementHalfLength = (Track->TrackElementAt(847, NextTrackVectorPosition).Length01) / 2;
3840  NextSpeedLimit = Track->TrackElementAt(848, NextTrackVectorPosition).SpeedLimit01;
3841  }
3842  else
3843  {
3844  NextElementHalfLength = (Track->TrackElementAt(849, NextTrackVectorPosition).Length23) / 2;
3845  NextSpeedLimit = Track->TrackElementAt(850, NextTrackVectorPosition).SpeedLimit23;
3846  }
3847  }
3848  else if(NextEntryPos == 1)
3849  {
3850  NextElementHalfLength = (Track->TrackElementAt(851, NextTrackVectorPosition).Length01) / 2;
3851  NextSpeedLimit = Track->TrackElementAt(852, NextTrackVectorPosition).SpeedLimit01;
3852  }
3853  else // == 3
3854  {
3855  NextElementHalfLength = (Track->TrackElementAt(853, NextTrackVectorPosition).Length23) / 2;
3856  NextSpeedLimit = Track->TrackElementAt(854, NextTrackVectorPosition).SpeedLimit23;
3857  }
3858  }
3859  else
3860  {
3861  if(NextEntryPos > 1)
3862  {
3863  NextElementHalfLength = (Track->TrackElementAt(360, NextTrackVectorPosition).Length23) / 2;
3864  NextSpeedLimit = Track->TrackElementAt(361, NextTrackVectorPosition).SpeedLimit23;
3865  }
3866  else
3867  {
3868  NextElementHalfLength = (Track->TrackElementAt(362, NextTrackVectorPosition).Length01) / 2;
3869  NextSpeedLimit = Track->TrackElementAt(363, NextTrackVectorPosition).SpeedLimit01;
3870  }
3871  }
3872  }
3873  else
3874  {
3875  throw Exception("Error - Trying to access NextTrackVectorPosition when none present in SetTrainMovementValues");
3876  }
3877  // now check for stops, first cover those where don't want to add in length of next element
3878  // check if next element is a red signal - Attr 0,
3879  // note that this doesn't apply to trains stopped at a red signal since the signal position is
3880  // CurrentTrackVectorPosition not NextTrackVectorPosition
3881  bool StopRequired;
3882  if(Track->TrackElementAt(364, NextTrackVectorPosition).Config[Track->GetNonPointsOppositeLinkPos(NextEntryPos)] == Signal)
3883  {
3884  if(Track->TrackElementAt(365, NextTrackVectorPosition).Attribute == 0)
3885  {
3886  // no need to add in the length of element to CumulativeLength
3887  RedSignalFlag = true;
3888  }
3889  // next element is a red signal
3890  }
3891  // check if current element is a station & next element contains a train - trains will always stop without crashing at a
3892  // station they are due to stop at even if there is a train in front blocking the normal stop position - providing there is
3893  // at least one platform element free
3895  CurrentTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && Track->OtherTrainOnTrack(3, NextTrackVectorPosition,
3896  NextEntryPos, TrainID))
3897  {
3898  // no need to add in the length of element to CumulativeLength
3899  if(StopRequired)
3900  {
3901  StationFlag = true;
3902  }
3903  }
3904  // check if next element contains a train & in Signaller mode (always stops for train in front if in signaller mode)
3905  else if((TrainMode == Signaller) && Track->OtherTrainOnTrack(4, NextTrackVectorPosition, NextEntryPos, TrainID))
3906  // (Track->TrackElementAt(651, NextTrackVectorPosition).TrainIDOnElement > -1))
3907  {
3908  // no need to add in the length of element to CumulativeLength
3909  TrainInFrontInSignallerModeFlag = true;
3910  }
3911  // check if next element is a buffer
3912  else if(Track->TrackElementAt(366, NextTrackVectorPosition).TrackType == Buffers)
3913  {
3914  // need to add in the length of that element to CumulativeLength
3915  CumulativeLength += Track->TrackElementAt(367, NextTrackVectorPosition).Length01;
3916  BuffersFlag = true;
3917  }
3918  // check if next element is a station stop
3920  NextTrackVectorPosition).ActiveTrackElementName, StopRequired) > -1) && ((Track->TrackElementAt(371,
3921  NextTrackVectorPosition).StationEntryStopLinkPos1 == EntryPos) || (Track->TrackElementAt(372,
3922  NextTrackVectorPosition).StationEntryStopLinkPos2 == EntryPos)))
3923  {
3924  // need to add in the length of that element to CumulativeLength if a stop required
3925  if(StopRequired)
3926  {
3927  StationFlag = true;
3928  CumulativeLength += Track->TrackElementAt(373, NextTrackVectorPosition).Length01;
3929  }
3930  }
3931  }
3932  //now can decide whether need to stop over CumulativeLength
3933  if(RedSignalFlag || BuffersFlag || StationFlag || TrainInFrontInSignallerModeFlag || SignallerStopRequired || StepForwardFlag) // no more loops
3934  {
3935  // have to come to a stop over CumulativeLength
3936  if(CumulativeLength == FrontElementLength)
3937  // will be if StepForwardFlag (if stopped to begin with on zero power then earlier check will intercept it and it won't reach here
3938  // only one length to go before stop so check whether need to accelerate for half length then brake for latter
3939  // half; calc speed at halfway point that corresponds to half braking rate for latter half of track element,
3940  // and if less than EntrySpeed then skip this section (don't need any acceleration)
3941  // if not calc speed at halfway point & if less than above set half speed to this value;
3942  // use constant acceleration in calculating half time point
3943  {
3944  MaxExitSpeed = 0;
3945  double MaxHalfSpeed;
3946  double MaxHalfSpeedAtHalfBraking = 3.6 * sqrt(MaxBrakeRate * FrontElementLength / 2);
3947  // have to halve the element length, & can't be zero or negative so no need to test
3948  // if(MaxHalfSpeedAtHalfBraking > LimitingSpeed) MaxHalfSpeed = LimitingSpeed; else MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3949  if(MaxHalfSpeedAtHalfBraking > FrontElementMaxSpeed)
3950  {
3951  MaxHalfSpeed = FrontElementMaxSpeed;
3952  }
3953  else
3954  {
3955  MaxHalfSpeed = MaxHalfSpeedAtHalfBraking;
3956  }
3957  if(MaxHalfSpeed > (2 * EntrySpeed))
3958  // use 2x to prevent kangarooing at last element when had
3959  // been braking smoothly at less that 50% braking rate, 2x should prevent all but extreme cases
3960  {
3961  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
3962  0.333334);
3963  bool HalfSpeedLimited = false;
3964  if(MaxHalfSpeed < ExitSpeedHalf)
3965  {
3966  ExitSpeedHalf = MaxHalfSpeed;
3967  HalfSpeedLimited = true;
3968  }
3969  if(PowerAtRail > 1)
3970  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
3971  {
3972  // [km/h/3.6 = m/s]
3973  ExitTimeHalf =
3974  EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
3975  }
3976  else
3977  {
3978  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
3979  }
3980  // the above is the time taken to accelerate to ExitSpeedHalf, so if this is reached before the half
3981  // way point (i.e. HalfSpeedLimited is set) then the time will be too short; change later to equal the
3982  // braking time; not fully accurate but better to be equal than have a short acceleration period followed
3983  // by a long braking period
3984  ExitSpeedFull = 0;
3985  TempBrakeRate = ExitSpeedHalf * ExitSpeedHalf / 2 / 3.6 / 3.6 / EntryHalfLength;
3986  if(TempBrakeRate > MaxBrakeRate)
3987  {
3988  TempBrakeRate = MaxBrakeRate;
3989  }
3990  // shouldn't be but leave in anyway
3991  if(TempBrakeRate > BrakeRate)
3992  {
3993  BrakeRate = TempBrakeRate;
3994  }
3995  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
3996  ExitTimeFull = ExitTimeHalf + TDateTime(ExitSpeedHalf / 3.6 / BrakeRate / 86400.0);
3997  if(HalfSpeedLimited)
3998  // this is the change referred to above
3999  {
4000  TDateTime BrakingTime = ExitTimeFull - ExitTimeHalf;
4001  ExitTimeHalf = EntryTime + BrakingTime;
4002  ExitTimeFull = ExitTimeHalf + BrakingTime;
4003  }
4004  OneLengthAccelDecel = true; //used in TrackTrainFloat in InterfaceUnit.cpp to show accelerating for first half move then decelerating
4005  Utilities->CallLogPop(1095);
4006  return;
4007  }
4008  }
4009  // set braking to achieve speed = 0 @ CumulativeLength up to MaxBrakeRate
4010  // calc MaxExitSpeed for element at EntryPosition & set to this or existing val if lower,
4011  // calc th, tf, sh, & sf
4012  TempBrakeRate = EntrySpeed * EntrySpeed / 2 / 3.6 / 3.6 / CumulativeLength;
4013  if(TempBrakeRate > MaxBrakeRate)
4014  {
4015  TempBrakeRate = MaxBrakeRate;
4016  }
4017  if(TempBrakeRate > BrakeRate)
4018  {
4019  BrakeRate = TempBrakeRate;
4020  }
4021  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4022  if(SignallerStopRequired)
4023  // set BrakeRate to max of its calculated value or SignallerStopBrakeRate
4024  {
4026  {
4028  // this prevents the brakerate from reducing for a signaller stop
4029  // regardless of other conditions that may change as progress round the loop
4030  }
4031  }
4033  // prevents BrakeRate dropping below SignallerStopBrakeRate once it's been set whether or not SignallerStopRequired set
4034  // SignallerStopRequired may not be set if a red signal found in a later calc, & brakerate may then drop
4035  {
4037  }
4038  int TempMaxExitSpeed;
4039  // calc current value & if less than MaxExitSpeed set that to this
4040  MaxExitSpeedAtHalfBrakingSquared = 3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength);
4041  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4042  {
4043  MaxExitSpeedAtHalfBraking = 0;
4044  }
4045  else
4046  {
4047  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4048  }
4049  // if(MaxExitSpeedAtHalfBraking > LimitingSpeed) MaxExitSpeed = LimitingSpeed; else MaxExitSpeed = MaxExitSpeedAtHalfBraking;
4050  // I think the above was dropped because it could cause MaxExitSpeed to increase (MaxExitSpeed is an external variable retained between loops)
4051  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4052  {
4053  TempMaxExitSpeed = FrontElementMaxSpeed;
4054  }
4055  else
4056  {
4057  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4058  }
4059  if(TempMaxExitSpeed < MaxExitSpeed)
4060  {
4061  MaxExitSpeed = TempMaxExitSpeed;
4062  }
4063  // here have EntrySpeed & MaxExitSpeed (for the next element), BrakeRate (to bring speed to zero over
4064  // Cumulativelength, and Cumulativelength
4065 
4066  if((EntrySpeed > MaxExitSpeed) || SignallerStopRequired || (SignallerStopBrakeRate > 0.01)) // need to brake
4067  {
4068  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4069  if(ExitSpeedHalfSquared < 10)
4070  {
4071  ExitSpeedHalf = 0;
4072  }
4073  else
4074  {
4075  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4076  }
4077  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4078  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4079  if(ExitSpeedFullSquared < 10)
4080  {
4081  ExitSpeedFull = 0;
4082  }
4083  else
4084  {
4085  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4086  }
4087  if((StationFlag) && (CumulativeLength == FrontElementLength))
4088  {
4089  ExitSpeedFull = 0;
4090  // force a stop for station (not for buffers or red signal)
4091  }
4092  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4093  }
4094  // new condition at v2.4.0
4095  else if(PowerAtRail <= 1)
4096  // use EntrySpeed, CumulativeLength & BrakeRate to calculate the half and full exit times and speeds for next element
4097  // avoid using AValue in denominator or have excessively long times
4098  {
4099  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * BrakeRate * FrontElementLength);
4100  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4101  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / (3.6 * BrakeRate * 86400.0));
4102 
4103  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * FrontElementLength);
4104  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4105  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / (3.6 * BrakeRate * 86400.0));
4106  }
4107  else // e.g. moving towards a signal or station after a speed limit, so can accelerate unless no power
4108  // without the power need above condition or have hours of delay times, above added at v2.4.0
4109  {
4110  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4111  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/(3.6^3))^0.333334;
4112  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph, S = distance & T = time, note that km/h/3.6 = m/s
4113  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4114  BrakeRate = 0;
4115  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)),
4116  0.333334);
4117  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4118  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4120  // can accelerate continually over the half length
4121  {
4122  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4123  / 86400.0);
4125  // can accelerate continually over the full length
4126  // i.e both (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4127  {
4128  ExitTimeFull =
4129  EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue) / 86400.0);
4130  }
4131  else // (ExitSpeedHalf <= MaxExitSpeed) but (ExitSpeedFull > MaxExitSpeed)
4132  // accelerate to MaxExitSpeed then reamin at this speed for rest of element
4133  {
4134  // added at v0.6 as a safeguard
4135  if(MaxExitSpeed < EntrySpeed)
4136  {
4138  }
4139  // to prevent DeltaExitTimeToMaxInSecs being negative
4140  if(MaxExitSpeed < 1)
4141  {
4142  MaxExitSpeed = 1;
4143  }
4144  // to prevent divide by zero error
4145  // below as was
4147  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4148  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4149  (1.5 * AValue * AValue);
4150  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4151  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4152  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4153  }
4154  }
4155  else // ExitSpeedHalf > MaxExitSpeed, so ExitSpeedFull must also be > MaxExitSpeed
4156  // accelerate over first half to MaxExitSpeed then remain at this speed for rest of the first and
4157  // second halves of the element
4158  {
4159  // added at v0.6 as a safeguard
4160  if(MaxExitSpeed < EntrySpeed)
4161  {
4163  }
4164  // to prevent DeltaExitTimeToMaxInSecs being negative
4165  if(MaxExitSpeed < 1)
4166  {
4167  MaxExitSpeed = 1; // to prevent divide by zero error
4168  }
4169  // below as was
4171  double DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4172  double DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4173  (1.5 * AValue * AValue);
4174  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax;
4175  // remaining distance to half length
4176  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4177  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4179  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4180  }
4181  }
4182  Utilities->CallLogPop(706);
4183  return;
4184  }
4185  else
4186  {
4187  if(!BuffersOrContinuationNowFlag)
4188  {
4189  if(NextSpeedLimit < LimitingSpeed)
4190  {
4191  LimitingSpeed = NextSpeedLimit;
4192  }
4193  }
4194  // calc max exit speed at half braking to ensure don't accelerate past it (if acceleration is required)
4195  int TempMaxExitSpeed;
4196  // calc current value & if less than MaxExitSpeed set that to this
4197  // Note that LimitingSpeed is the max value at the end of CumulativeLength, so MaxExitSpeedAtHalfBrakingSquared will be larger
4198  MaxExitSpeedAtHalfBrakingSquared = (LimitingSpeed * LimitingSpeed) + (3.6 * 3.6 * MaxBrakeRate * (CumulativeLength - FrontElementLength));
4199  if(MaxExitSpeedAtHalfBrakingSquared < 10)
4200  {
4201  MaxExitSpeedAtHalfBraking = 0;
4202  }
4203  else
4204  {
4205  MaxExitSpeedAtHalfBraking = sqrt(MaxExitSpeedAtHalfBrakingSquared);
4206  }
4207  if(MaxExitSpeedAtHalfBraking > FrontElementMaxSpeed)
4208  {
4209  TempMaxExitSpeed = FrontElementMaxSpeed;
4210  }
4211  else
4212  {
4213  TempMaxExitSpeed = MaxExitSpeedAtHalfBraking;
4214  }
4215  if(TempMaxExitSpeed < MaxExitSpeed)
4216  {
4217  MaxExitSpeed = TempMaxExitSpeed;
4218  }
4219  // MaxExitSpeed is an external variable & this can reduce its value
4220  if(EntrySpeed > LimitingSpeed)
4221  // note that LimitingSpeed is more restrictive than MaxExitSpeed
4222  // calc TempBrakeRate & set BrakeRate to this or keep existing val if higher
4223  {
4224  TempBrakeRate = ((EntrySpeed * EntrySpeed) - (LimitingSpeed * LimitingSpeed)) / 3.6 / 3.6 / 2 / CumulativeLength;
4225  if(TempBrakeRate > MaxBrakeRate)
4226  {
4227  TempBrakeRate = MaxBrakeRate;
4228  }
4229  // shouldn't be for speedlimits since all known in advance, but include anyway
4230  if(TempBrakeRate > BrakeRate)
4231  {
4232  BrakeRate = TempBrakeRate;
4233  }
4234  // BrakeRate may already have been set in an earlier loop so don't want to reduce it
4235  }
4236  }
4237  if(!BuffersOrContinuationNowFlag)
4238  {
4239  CurrentTrackVectorPosition = NextTrackVectorPosition;
4240  EntryPos = NextEntryPos;
4241  CurrentElementHalfLength = NextElementHalfLength;
4242  if(Track->TrackElementAt(378, NextTrackVectorPosition).TrackType == Continuation)
4243  {
4244  ContinuationNextFlag = true;
4245  }
4246  }
4247  }
4248  while(((CumulativeLength - FrontElementLength) < DistanceAtHalfBraking) && ((!BuffersOrContinuationNowFlag && !ContinuationNextFlag) ||
4250  // check from the end of the front element, if include the front element and could brake during it, then will skip further loops
4251  // & miss a stop requirement just beyond the front element. happened in Richard Standing's railway where a new service introduced
4252  // on a 100m length, with 20m length after & then a red signal - train accelerated over the 100m then caused a SPAD as too short a
4253  // stopping distance after it.
4254 
4255  //(!BuffersOrContinuationNowFlag && !ContinuationNextFlag) true when no continuation on either the next element and next but one element.
4256  //There won't be a buffer on the next element or would have caught earlier, just using this flag for convenience.
4257 
4258  // If SignallerStoppingFlag then don't exit loop because of an imminent continuation, because continuation
4259  // not immediately in front (if it is then LeadElement will be the continuation & SignallerStoppingFlag will be reset in UpdateTrain()),
4260  // need to at least give a chance to stop on signaller command, if keep moving until continuation is immediately in front then will
4261  // exit loop & that is OK as don't want to stop so close to a continuation, if that happens it means that the command to stop was given
4262  // too late
4263 
4264  // set final braking or acc'n speed & time values
4265  if(BrakeRate > 0.01)
4266  {
4267  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4268  if(ExitSpeedHalfSquared < 10)
4269  {
4270  ExitSpeedHalf = 0;
4271  }
4272  else
4273  {
4274  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4275  }
4276  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4277  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4278  if(ExitSpeedFullSquared < 10)
4279  {
4280  ExitSpeedFull = 0;
4281  }
4282  else
4283  {
4284  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4285  }
4286  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4287  }
4288  else
4289  {
4290  // for accelerating the energy input rate (PowerAtRail) is constant so the following formuli are used:-
4291  // (1) V^2/(3.6^2) - U^2/(3.6^2) = A^2T; (2) V = 3.6 * ((1.5*S*A^2) + U^3/ (3.6)^3)^0.333334;
4292  // where A is a constant (2*PowerAtRail/Mass)^0.5; V = final speed in kph, U = initial speed in kph , S = distance in m & T = time
4293  // This is a bit unrealistic during the early acceleration phase as it will be too rapid, but shouldn't affect the running unduly
4294 
4295  BrakeRate = 0;
4296  ExitSpeedHalf = 3.6 * Power(((1.5 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4297  ExitSpeedFull = 3.6 * Power(((3 * EntryHalfLength * AValue * AValue) + (EntrySpeed * EntrySpeed * EntrySpeed / 3.6 / 3.6 / 3.6)), 0.333334);
4298  // above valid for ExitSpeedHalf & Full <= MaxExitSpeed
4300  {
4301  if(PowerAtRail > 1)
4302  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4303  {
4304  // [km/h/3.6 = m/s]
4305  ExitTimeHalf = EntryTime + TDateTime(((ExitSpeedHalf * ExitSpeedHalf) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4306  / 86400.0);
4307  }
4308  else
4309  {
4310  ExitTimeHalf = EntryTime + TDateTime(EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4311  }
4313  // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull <= MaxExitSpeed)
4314  {
4315  if(PowerAtRail > 1)
4316  // added at v2.4.0 in case reach here with failed train, when can't use AValue in denominator as close zero
4317  {
4318  // [km/h/3.6 = m/s]
4319  ExitTimeFull = EntryTime + TDateTime(((ExitSpeedFull * ExitSpeedFull) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue)
4320  / 86400.0);
4321  }
4322  else
4323  {
4324  ExitTimeFull = EntryTime + TDateTime(2 * EntryHalfLength * 3.6 / EntrySpeed / 86400.0);
4325  }
4326  }
4327  else // (ExitSpeedHalf <= MaxExitSpeed) & (ExitSpeedFull > MaxExitSpeed)
4328  {
4329  // added at v0.6 as a safeguard
4330  if(MaxExitSpeed < EntrySpeed)
4331  {
4333  }
4334  // to prevent DeltaExitTimeToMaxInSecs being negative
4335  if(MaxExitSpeed < 1)
4336  {
4337  MaxExitSpeed = 1; // to prevent divide by zero error
4338  }
4339  // below as was
4341  double DeltaExitTimeToMaxInSecs;
4342  double DistanceToMax;
4343  if(PowerAtRail > 1) // added at v2.4.0
4344  {
4345  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4346  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4347  (1.5 * AValue * AValue);
4348  }
4349  else // shouldn't ever get here given that ExitSpeedFull > ExitSpeedHalf
4350  {
4351  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4352  // these not really accurate but will be good enough
4353  DistanceToMax = EntryHalfLength;
4354  }
4355  double RemainingDistance = double(FrontElementLength) - DistanceToMax;
4356  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4357  ExitTimeFull = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4358  }
4359  }
4360  else // ExitSpeedHalf > MaxExitSpeed, ExitSpeedFull must be > MaxExitSpeed
4361  {
4362  // added at v0.6 as a safeguard
4363  if(MaxExitSpeed < EntrySpeed)
4364  {
4366  }
4367  // to prevent DeltaExitTimeToMaxInSecs being negative
4368  if(MaxExitSpeed < 1)
4369  {
4370  MaxExitSpeed = 1; // to prevent divide by zero error
4371  }
4372  // below as was
4374  double DeltaExitTimeToMaxInSecs;
4375  double DistanceToMax;
4376  if(PowerAtRail > 1) // added at v2.4.0
4377  {
4378  DeltaExitTimeToMaxInSecs = ((MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / (AValue * AValue);
4379  DistanceToMax = ((MaxExitSpeed * MaxExitSpeed * MaxExitSpeed) - (EntrySpeed * EntrySpeed * EntrySpeed)) / 3.6 / 3.6 / 3.6 /
4380  (1.5 * AValue * AValue);
4381  }
4382  else
4383  {
4384  DeltaExitTimeToMaxInSecs = 2 * EntryHalfLength * 3.6 / EntrySpeed;
4385  // these not really accurate but will be good enough
4386  DistanceToMax = EntryHalfLength / 2;
4387  }
4388  double RemainingDistance = double(FrontElementLength / 2) - DistanceToMax; // remaining distance to half length
4389  double DeltaRemainingTimeInSecs = 3.6 * RemainingDistance / MaxExitSpeed;
4390  ExitTimeHalf = EntryTime + TDateTime((DeltaExitTimeToMaxInSecs + DeltaRemainingTimeInSecs) / 86400.0);
4392  ExitTimeFull = ExitTimeHalf + TDateTime(3.6 * EntryHalfLength / MaxExitSpeed / 86400.0);
4393  }
4394  }
4395  }
4396 
4397  else // SPADFlag set
4398  {
4400  ExitSpeedHalfSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 2 * BrakeRate * EntryHalfLength);
4401  if(ExitSpeedHalfSquared < 10)
4402  {
4403  ExitSpeedHalf = 0;
4404  }
4405  else
4406  {
4407  ExitSpeedHalf = sqrt(ExitSpeedHalfSquared);
4408  }
4409  ExitTimeHalf = EntryTime + TDateTime((EntrySpeed - ExitSpeedHalf) / 3.6 / BrakeRate / 86400.0);
4410  ExitSpeedFullSquared = (EntrySpeed * EntrySpeed) - (3.6 * 3.6 * 4 * BrakeRate * EntryHalfLength);
4411  if(ExitSpeedFullSquared < 10)
4412  {
4413  ExitSpeedFull = 0;
4414  }
4415  else
4416  {
4417  ExitSpeedFull = sqrt(ExitSpeedFullSquared);
4418  }
4419  ExitTimeFull = EntryTime + TDateTime((EntrySpeed - ExitSpeedFull) / 3.6 / BrakeRate / 86400.0);
4420 
4421  // check if the exit speed is < 80% of the stopping speed for the next element, and if so stop at end of this element
4422  // this is because would stop short of end of next element (in reality the time to reach the end of the next element
4423  // would be too short (could be so short as to make the train jump) as time is calculated purely on speed & brake rate);
4424  // 80% is used as the brake rate might be set to come to a halt at the end of the next element in which case the speed
4425  // will be the stopping speed.
4426  if(ExitSpeedFull > 0)
4427  {
4428  if(Track->TrackElementAt(746, CurrentTrackVectorPosition).TrackType == Points)
4429  {
4430  if((EntryPos == 0) || (EntryPos == 2))
4431  {
4432  if(Track->TrackElementAt(747, CurrentTrackVectorPosition).Attribute == 0)
4433  {
4434  ExitPos = 1;
4435  }
4436  else
4437  {
4438  ExitPos = 3;
4439  }
4440  }
4441  else
4442  {
4443  ExitPos = 0;
4444  }
4445  }
4446  else
4447  {
4448  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4449  }
4450  NextTrackVectorPosition = Track->TrackElementAt(748, CurrentTrackVectorPosition).Conn[ExitPos];
4451  NextEntryPos = Track->TrackElementAt(749, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4452  if(NextTrackVectorPosition > -1) // not a continuation or buffer
4453  {
4454  int NextElementLength;
4455  if(NextEntryPos > 1)
4456  {
4457  NextElementLength = (Track->TrackElementAt(750, NextTrackVectorPosition).Length23);
4458  }
4459  else
4460  {
4461  NextElementLength = (Track->TrackElementAt(751, NextTrackVectorPosition).Length01);
4462  }
4463  double NextStoppingSpeed = sqrt(3.6 * 3.6 * 2 * BrakeRate * NextElementLength);
4464  if(ExitSpeedFull < (0.8 * NextStoppingSpeed))
4465  {
4466  ExitSpeedFull = 0;
4467  }
4468  }
4469  }
4470  }
4471  // allow all values to be set normally in case need to brake, then test for zero power & need to coast
4472  if(PowerAtRail < 1) // new at v2.4.0 note that km/h/3.6 = m/s
4473  {
4474  // bring to a stop in 20 elements at 100km/h & assume each 100m long for calculating exit times but if on a continuation maintain speed
4475  if(LeadElement > -1)
4476  {
4478  // don't stop on a continuation either entering or leaving
4479  {
4482  MaxExitSpeed = LimitingSpeed;
4483  BrakeRate = 0;
4484  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4485  // assume length is 50m for ease of calc
4486  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4487  Utilities->CallLogPop(2126);
4488  return;
4489  }
4490  }
4491  else if(MidElement > -1)
4492  {
4494  {
4497  MaxExitSpeed = LimitingSpeed;
4498  BrakeRate = 0;
4499  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4500  // assume length is 50m for ease of calc
4501  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4502  Utilities->CallLogPop(2127);
4503  return;
4504  }
4505  }
4506  else if(LagElement > -1)
4507  {
4509  {
4512  MaxExitSpeed = LimitingSpeed;
4513  BrakeRate = 0;
4514  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed) / 86400.0));
4515  // assume length is 50m for ease of calc
4516  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf) / 86400.0));
4517  Utilities->CallLogPop(2128);
4518  return;
4519  }
4520  }
4521  if(EntrySpeed > 7.5) // keep going for at least another element
4522  {
4523  ExitSpeedHalf = EntrySpeed - 2.5;
4524  ExitSpeedFull = EntrySpeed - 5;
4525  MaxExitSpeed = LimitingSpeed;
4526  BrakeRate = 0;
4527  ExitTimeHalf = EntryTime + TDateTime((50 * 3.6 / (EntrySpeed - 1.25) / 86400.0));
4528  // assume length is 50m for ease of calc
4529  ExitTimeFull = ExitTimeHalf + TDateTime((50 * 3.6 / (ExitSpeedHalf - 1.25) / 86400.0));
4530  Utilities->CallLogPop(2129);
4531  return;
4532  }
4533  else // stop immediately, don't enter next element, floating label had displayed last exit speed full on 2nd half move so with zero when fully on element
4534  {
4535  // will appear to have slowed at steady rate
4536  ExitSpeedHalf = 0;
4537  ExitSpeedFull = 0;
4538  MaxExitSpeed = LimitingSpeed;
4539  BrakeRate = 0;
4540  ExitTimeHalf = EntryTime + TDateTime(1/24); //set this high in case used later though unlikely
4541  ExitTimeFull = EntryTime + TDateTime(1/23); //set about 2.5 mins later than half time
4542  StoppedWithoutPower = true;
4543  Utilities->CallLogPop(2130);
4544  return;
4545  }
4546  }
4547  // TempBrakeRate=MinSingle; TempBrakeRate=MaxSingle; TempBrakeRate=MinDouble; TempBrakeRate=MaxDouble;//included to stop warnings from unused declarations in math.hpp
4548  // TempBrakeRate=MinExtended; TempBrakeRate=MaxExtended; TempBrakeRate=MinComp; TempBrakeRate=MaxComp;//included to stop warnings from unused declarations in math.hpp
4549  Utilities->CallLogPop(707);
4550 }
4551 // ---------------------------------------------------------------------------
4552 /*
4553  bool TTrain::IsTerminalStation(int TrackVectorPosition, int EntryPos)
4554  {
4555  int NextExitPos;
4556  TTrackElement NextElement = Track->TrackElementAt(379, TrackVectorPosition), TempElement;
4557  if(TimetableVector.empty()) return false;
4558  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4559  while((NextElement.ActiveTrackElementName == TimetableVector.begin()->LocationName) && (NextElement.TrackType != Buffers))
4560  {
4561  //check for points & follow attribute, but don't worry about a derail as that dealt with elsewhere
4562  if((NextElement.TrackType != Points) || ((EntryPos != 0) && (EntryPos != 2)))
4563  {
4564  NextExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4565  }
4566  else if((NextElement.TrackType == Points) && ((EntryPos == 0) || (EntryPos == 2)))
4567  {
4568  if(NextElement.Attribute == 0) NextExitPos = 1; else NextExitPos = 3;
4569  }
4570  TempElement = Track->TrackElementAt(380, NextElement.Conn[NextExitPos]);//need temp as NextElement used in next step
4571  NextElement = TempElement;
4572  }
4573  if(NextElement.ActiveTrackElementName != TimetableVector.begin()->LocationName) return false;
4574  if(NextElement.TrackType == Buffers) return true;
4575  return false;//shouldn't reach here but include to prevent compiler return warning
4576  }
4577 */
4578 // ---------------------------------------------------------------------------
4579 
4580 int TTrain::NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
4581 /*
4582  returns the number by which the train ActionVectorEntryPtr needs
4583  to be incremented to point to the location arrival entry or passtime entry before a change of direction. Used to display missed
4584  actions when a stop or pass location has been reached before other timetabled events have been carried out. If can't find it, or Name
4585  is "", -1 is returned. A change of direction is the limit of the search because a train may not stop at a location on the way out
4586  but stop on way back, and in these circumstances no actions have been missed. Stop indicates whether the train will stop at (true)
4587  or pass (false) the location.
4588 */{
4589  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NameInTimetableBeforeCDT," + Name + "," + HeadCode);
4590  Stop = false;
4591  if(TimetableFinished || (Name == ""))
4592  {
4593  Utilities->CallLogPop(957);
4594  return(-1);
4595  }
4597 /*added the following check at v2.11.1 because of a fault found in Kevin Smith's railway (notified 02/01/22). CH01 started at
4598 Chester behind the stop position, but when it departed and this function was called it found Chester again with no cdt
4599 before it (it went round the Liverpool Loop), so it stopped again when it reached the stop position and reported all intermediate stops as having been
4600 missed. This check is for the train just having departed from the station in question, and if it has then any further stations
4601 with the same name are ignored - i.e. it stops a train from stopping at the same station twice in succession.
4602 */
4603  if(Ptr > &TrainDataEntryPtr->ActionVector.at(0))
4604  {
4605  Ptr--;
4606  if((Ptr->DepartureTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4607  {
4608  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4609  {
4610  Utilities->CallLogPop(2444);
4611  return(-1);
4612  }
4613  }
4614  }
4615  // start looking from current pointer position
4616  for(TActionVectorEntry *Ptr = ActionVectorEntryPtr; Ptr < &TrainDataEntryPtr->ActionVector.back(); Ptr++)
4617  {
4618  if((Ptr->Command == "cdt") || (Ptr->FormatType == Repeat))
4619  {
4620  break;
4621  }
4622  if((Ptr->ArrivalTime > TDateTime(-1)) && (Ptr->LocationName == Name))
4623  {
4624  if((Ptr->FormatType == TimeLoc) || (Ptr->FormatType == TimeTimeLoc))
4625  {
4626  Stop = true;
4627  Utilities->CallLogPop(960);
4628  return (Ptr - ActionVectorEntryPtr);
4629  }
4630  }
4631  if((Ptr->EventTime > TDateTime(-1)) && (Ptr->LocationName == Name) && (Ptr->Command == "pas"))
4632  {
4633  Utilities->CallLogPop(1517);
4634  return (Ptr - ActionVectorEntryPtr);
4635  }
4636  }
4637  Utilities->CallLogPop(959);
4638  return(-1); // not found a valid entry
4639 }
4640 
4641 // ---------------------------------------------------------------------------
4642 
4644 /* Checks forward from train LeadElement, following leading point attributes but ignoring trailing point attributes,
4645  until finds either a train or a signal/buffers/continuation/loop. If finds a train returns false, else returns true.
4646  Ignores the call-on signal.
4647 */{
4648  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ClearToNextSignal" + "," + HeadCode);
4649  int ReturnVal = 0;
4650  int ElementCount = 0;
4651 /* dropped at v2.12.0 as takes up a great deal of time unnecessarily - substitute 1000 elements instead and return true (very unlikley to need to search this far [10km at min length])
4652  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
4653  {
4654  Track->TrackElementAt(1031, x).TempTrackMarker01 = false;
4655  Track->TrackElementAt(1032, x).TempTrackMarker23 = false;
4656  }
4657 */
4658  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition;
4659  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos;
4660 
4661  while(true)
4662  {
4663  if((Track->TrackElementAt(382, CurrentTrackVectorPosition).TrainIDOnElement > -1) && (Track->TrackElementAt(383,
4664  CurrentTrackVectorPosition).TrainIDOnElement != TrainID))
4665  {
4666  ReturnVal = 1;
4667  break;
4668  }
4669  if(((Track->TrackElementAt(384, CurrentTrackVectorPosition).TrackType == Buffers) || (Track->TrackElementAt(385,
4670  CurrentTrackVectorPosition).TrackType == Continuation)) && (EntryPos == 1))
4671  {
4672  ReturnVal = 2;
4673  break;
4674  }
4675  if((EntryPos < 2) && (Track->TrackElementAt(386, CurrentTrackVectorPosition).Config[1 - EntryPos] == Signal) && (Track->TrackElementAt(529,
4676  CurrentTrackVectorPosition).Attribute != 4)) // Attr 4 == call-on signal
4677  {
4678  ReturnVal = 3;
4679  break;
4680  }
4681 /* not needed at and after v2.12.0, see above
4682  if((Track->TrackElementAt(387, CurrentTrackVectorPosition).TrackType == Bridge) || (Track->TrackElementAt(388, CurrentTrackVectorPosition).TrackType == Crossover))
4683  {
4684  if((EntryPos < 2) && (Track->TrackElementAt(523, CurrentTrackVectorPosition).TempTrackMarker01))
4685  // must be a loop - reached same point as examined earlier
4686  {
4687  ReturnVal = 4;
4688  break;
4689  }
4690  else if((EntryPos > 1) && (Track->TrackElementAt(524, CurrentTrackVectorPosition).TempTrackMarker23))
4691  {
4692  ReturnVal = 4;
4693  break;
4694  }
4695  }
4696  else
4697  {
4698  if((Track->TrackElementAt(525, CurrentTrackVectorPosition).TempTrackMarker01) || (Track->TrackElementAt(526, CurrentTrackVectorPosition).TempTrackMarker23))
4699  {
4700  ReturnVal = 4;
4701  break;
4702  }
4703  }
4704  if(EntryPos < 2)
4705  {
4706  Track->TrackElementAt(389, CurrentTrackVectorPosition).TempTrackMarker01 = true;
4707  }
4708  else
4709  {
4710  Track->TrackElementAt(527, CurrentTrackVectorPosition).TempTrackMarker23 = true;
4711  }
4712 */
4713 
4714  if(Track->TrackElementAt(390, CurrentTrackVectorPosition).TrackType == Points)
4715  {
4716  if((EntryPos == 0) || (EntryPos == 2))
4717  {
4718  if(Track->TrackElementAt(391, CurrentTrackVectorPosition).Attribute == 0)
4719  {
4720  ExitPos = 1;
4721  }
4722  else
4723  {
4724  ExitPos = 3;
4725  }
4726  }
4727  else
4728  {
4729  ExitPos = 0;
4730  }
4731  }
4732  else
4733  {
4734  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4735  }
4736  NextTrackVectorPosition = Track->TrackElementAt(392, CurrentTrackVectorPosition).Conn[ExitPos];
4737  NextEntryPos = Track->TrackElementAt(393, CurrentTrackVectorPosition).ConnLinkPos[ExitPos];
4738  CurrentTrackVectorPosition = NextTrackVectorPosition;
4739  EntryPos = NextEntryPos;
4740  ElementCount++;
4741  if(ElementCount > 1000)
4742  {
4743  ReturnVal = 4;
4744  break;
4745  }
4746  }
4747  if(ReturnVal == 1)
4748  {
4749  Utilities->CallLogPop(708);
4750  return(false);
4751  }
4752  if(ReturnVal == 2)
4753  {
4754  Utilities->CallLogPop(709);
4755  return(true);
4756  }
4757  if(ReturnVal == 3)
4758  {
4759  Utilities->CallLogPop(946);
4760  return(true);
4761  }
4762  if(ReturnVal == 4)
4763  {
4764  Utilities->CallLogPop(947);
4765  return(true);
4766  }
4767  else
4768  {
4769  throw Exception("Error - failed to set ReturnVal in ClearToNextSignal()");
4770  }
4771 }
4772 
4773 // ---------------------------------------------------------------------------
4774 
4776 /*
4777  Check whether calling-on conditions met - a) approaching train has stopped at a signal but not at a location;
4778  b) if there is a facing train at the station, it is being held appropriately (must be awaiting a join (Fjo or jbo) or a
4779  change of direction (cdt), remaining here (Frh), or under signaller control);
4780  c) at least 1 platform available for the approaching train; d) points (if any) set for direct route into platform;
4781  e) approaching train is to stop at station; f) no more facing signals between train and platform; g) [dropped g]
4782  h) train in front preventing route being set far enough to release stop signal; i) train in front not exiting at continuation; j) signal must be within 4km of
4783  the stop platform; k) [dropped (k), now can set a reoute or part route into platform so can set points more easily.] l) no existing route conflicts with the route into the platform,
4784  m) not failed or stopped without power
4785  If all OK & route or part route not already set then set an unrestricted route into the station (just to the first platform), to prevent point changing or other route conflicts - if a partial route set than can still
4786  change points outside the route or have a route conflict if another route is set.
4787 */{
4788  if(Track->RouteFlashFlag || TrainFailed || StoppedWithoutPower) //failed & no power conditions added at v2.10.0
4789  {
4790  return(false); // don't want to create a new route from the stop signal if one is already in construction & can't call on if failed or no power
4791  }
4792  // some of the callingon route elements may be involved
4793  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CallingOnAllowed" + "," + HeadCode);
4794  bool PlatformFoundFlag = false, StopRequired = false, SkipRouteCheck = false, RouteOrPartRouteSet = false; // last added at v1.2.0
4795  int CurrentTrackVectorPosition = LeadElement, NextTrackVectorPosition, ElementNumber = 0, Distance = 0;
4796  int RouteStartPosition;
4797  // this is the track vector position of the start element for the new unrestricted route - one past the stop signal
4798  int PlatformPosition;
4799  // the track vector position of the first stop platfrom
4800  int EntryPos = LeadEntryPos, ExitPos, NextEntryPos, RouteID;
4801  // not used here
4802  AnsiString LeadStationName = Track->TrackElementAt(395, LeadElement).ActiveTrackElementName; // still OK even if ""
4803  int LeadElementDistance = Track->TrackElementAt(1017, LeadElement).Length01; //added after 2.7.0 as don't want to add this to overall distance since train has already covered this distance
4804  // use Length01, may be wrong for points/crossovers/bridges but unlikely to occur in practice
4805  // must be stopped at a signal but not at a location & still in timetable (a)
4807  // no need to check for SignallerStopped as this function only called in Timetable mode
4808  {
4809  Utilities->CallLogPop(711);
4810  return(false);
4811  }
4812  while(true)
4813  {
4814  TTrackElement &CurrentTrackElement = Track->TrackElementAt(396, CurrentTrackVectorPosition);
4815  // don't look further than 4km ahead (j)
4816  if(Distance > (4000 + LeadElementDistance))
4817  {
4818  Utilities->CallLogPop(967);
4819  return(false);
4820  }
4821  // if find another train on an element in front, before find a valid platform, return false (c)
4822  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && !PlatformFoundFlag)
4823  {
4824  Utilities->CallLogPop(713);
4825  return(false);
4826  }
4827  // if find another train in front when there is a valid platform (keep searching after find a platform as train may still
4828  // be facing later on)
4829  if((CurrentTrackElement.TrainIDOnElement > -1) && (CurrentTrackElement.TrainIDOnElement != TrainID) && PlatformFoundFlag)
4830  {
4831  // get LeadElement, if -1 return (could be exiting at continuation) (i)
4832  TTrain OtherTrain = TrainController->TrainVectorAtIdent(12, CurrentTrackElement.TrainIDOnElement);
4833  if(OtherTrain.LeadElement == -1)
4834  {
4835  Utilities->CallLogPop(714);
4836  return(false);
4837  }
4838  // if a facing train then make sure it is awaiting a join (Fjo or jbo) or a change of direction (cdt), or remaining here (Frh) (b)
4839  if(OtherTrain.LeadElement == CurrentTrackVectorPosition)
4840  {
4841  AnsiString OtherCommand = OtherTrain.ActionVectorEntryPtr->Command;
4842  if((OtherCommand == "Fjo") || (OtherCommand == "jbo") || (OtherCommand == "cdt") || (OtherCommand == "Frh") ||
4843  (OtherTrain.TrainMode == Signaller))
4844  {
4845  break;
4846  }
4847  else
4848  {
4849  Utilities->CallLogPop(955);
4850  return(false);
4851  }
4852  }
4853  else // (h)
4854  {
4855  break;
4856  }
4857  }
4858  // if reach buffers or exit continuation return false (can set route)
4859  if(((CurrentTrackElement.TrackType == Buffers) || (CurrentTrackElement.TrackType == Continuation)) && (EntryPos == 1))
4860  {
4861  Utilities->CallLogPop(716);
4862  return(false);
4863  }
4864  // if reach forward signal (other than the one the train is waiting at) return false (can set route) (f)
4865  if((EntryPos < 2) && (CurrentTrackElement.Config[1 - EntryPos] == Signal) && (CurrentTrackVectorPosition != Track->TrackElementAt(404,
4867  {
4868  Utilities->CallLogPop(717);
4869  return(false);
4870  }
4871  // if reach a location that isn't in timetable return false - drop this as still can't set a route
4872 /*
4873  if((Track->TrackElementAt(405, CurrentTrackVectorPosition).ActiveTrackElementName != "") && (Track->TrackElementAt(406, CurrentTrackVectorPosition).ActiveTrackElementName != LeadStationName) &&
4874  (NameInTimetableBeforeCDT(14, Track->TrackElementAt(407, CurrentTrackVectorPosition).ActiveTrackElementName) == -1))
4875  {
4876  Utilities->CallLogPop(718);
4877  return false;
4878  }
4879 */
4880  // if reach a location that is in timetable set PlatformFoundFlag (but not if position is points set to diverge) (e)
4881  if((CurrentTrackElement.ActiveTrackElementName != "") && (CurrentTrackElement.ActiveTrackElementName != LeadStationName) &&
4882  (NameInTimetableBeforeCDT(15, CurrentTrackElement.ActiveTrackElementName, StopRequired) > -1))
4883  {
4884  if(StopRequired)
4885  {
4886  if((CurrentTrackElement.TrackType != Points) || ((CurrentTrackElement.TrackType == Points) && (CurrentTrackElement.Attribute == 0)))
4887  {
4888  if(!PlatformFoundFlag)
4889  {
4890  PlatformPosition = CurrentTrackVectorPosition;
4891  }
4892  // ensure this only set once at first valid platform position - the unrestricted route will end here
4893  PlatformFoundFlag = true;
4894  }
4895  }
4896  }
4897  // Drop this below - was to prevent call-on if front train had left the station. Criterion now is not that front
4898  // train has to be at station but that has to be before the next forward signal
4899 /*
4900  if((Track->TrackElementAt(411, CurrentTrackVectorPosition).ActiveTrackElementName == "") && (PlatformFoundFlag))
4901  {
4902  Utilities->CallLogPop(719);
4903  return false;
4904  }
4905 */
4906  // make sure points are followed correctly (d) & set ExitPos
4907  if(CurrentTrackElement.TrackType == Points)
4908  {
4909  if((EntryPos == 0) || (EntryPos == 2))
4910  {
4911  if(CurrentTrackElement.Attribute == 0)
4912  {
4913  ExitPos = 1;
4914  }
4915  else
4916  {
4917  ExitPos = 3;
4918  }
4919  }
4920  if(EntryPos == 1)
4921  {
4922  if(CurrentTrackElement.Attribute == 0)
4923  {
4924  ExitPos = 0;
4925  }
4926  else
4927  {
4928  Utilities->CallLogPop(720);
4929  return(false);
4930  }
4931  }
4932  if(EntryPos == 3)
4933  {
4934  if(CurrentTrackElement.Attribute == 1)
4935  {
4936  ExitPos = 0;
4937  }
4938  else
4939  {
4940  Utilities->CallLogPop(721);
4941  return(false);
4942  }
4943  }
4944  }
4945  else
4946  {
4947  ExitPos = Track->GetNonPointsOppositeLinkPos(EntryPos);
4948  }
4949  // check existing routes - if element forward of the signal (ElementNumber == 2) is AutoSignals then OK without further checks as this route must extend to
4950  // the next signal so must at least reach the station, also if have another route set (must be unrestricted) from either the stop signal or the element after it
4951  // to or towards the platform (& all points set correctly) then OK, otherwise reject if (1) there are any route elements already set from element
4952  // forward of element after the signal to & including the first platform element (covers crossover with other route set) or (2) a fouled diagonal (k)
4953  if(ElementNumber < 2)
4954  {
4955  SkipRouteCheck = true;
4956  }
4957  else
4958  {
4959  SkipRouteCheck = false;
4960  }
4961  if(ElementNumber == 1) // the stop signal
4962  {
4963  RouteStartPosition = CurrentTrackVectorPosition;
4964  }
4965 /*
4966  if(ElementNumber == 2)
4967  {
4968  if(AllRoutes->GetRouteTypeAndNumber(18, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->AutoSigsRoute) AutoSigs = true;
4969  else AutoSigs = false;
4970  if(AllRoutes->GetRouteTypeAndNumber(25, CurrentTrackVectorPosition, EntryPos, RouteID) == AllRoutes->NotAutoSigsRoute) OtherFullRouteSet = true;
4971  }
4972 */
4973  if(ElementNumber > 1)
4974  {
4975  if(AllRoutes->GetRouteTypeAndNumber(26, CurrentTrackVectorPosition, EntryPos, RouteID) != AllRoutes->NoRoute)
4976  {
4977  RouteOrPartRouteSet = true;
4978  }
4979  else
4980  {
4981  RouteOrPartRouteSet = false;
4982  }
4983  }
4984  if(!SkipRouteCheck && !RouteOrPartRouteSet)
4985  {
4986  if(AllRoutes->TrackIsInARoute(16, CurrentTrackVectorPosition, EntryPos)) // must be a conflicting route
4987  {
4988  Utilities->CallLogPop(1859);
4989  return(false);
4990  }
4991  int ExitLink = CurrentTrackElement.Link[ExitPos];
4992  if((ExitLink == 1) || (ExitLink == 3) || (ExitLink == 7) || (ExitLink == 9))
4993  {
4994  if(AllRoutes->DiagonalFouledByRouteOrTrain(6, CurrentTrackElement.HLoc, CurrentTrackElement.VLoc, ExitLink))
4995  {
4996  Utilities->CallLogPop(1850);
4997  return(false);
4998  }
4999  }
5000  }
5001  // finished all checks, now update CurrentTrackVectorPosition & EntryPos for the next iteration
5002  if(EntryPos < 2)
5003  {
5004  Distance += CurrentTrackElement.Length01;
5005  }
5006  else
5007  {
5008  Distance += CurrentTrackElement.Length23;
5009  }
5010  NextTrackVectorPosition = CurrentTrackElement.Conn[ExitPos];
5011  NextEntryPos = CurrentTrackElement.ConnLinkPos[ExitPos];
5012  CurrentTrackVectorPosition = NextTrackVectorPosition;
5013  EntryPos = NextEntryPos;
5014  ElementNumber++;
5015  } // while(true)
5016 
5017  // if all OK & autosigs route not already set then set an unrestricted route into the station (just to the first platform)
5018  // from the stop signal (note that it may be last in an autosigs route)
5019  // a single element route at the stop signal should have been removed prior to this function being called (that called before
5020  // this in ClockTimer2)
5021 
5022  // now add elements to the CallonVector
5023  TAllRoutes::TCallonEntry CallonEntry(RouteOrPartRouteSet, RouteStartPosition, PlatformPosition);
5024 
5025  AllRoutes->CallonVector.push_back(CallonEntry);
5026  Utilities->CallLogPop(1860);
5027  return(true); // return false if fail to set route for any reason
5028 }
5029 
5030 // ---------------------------------------------------------------------------
5031 /*
5032  bool TTrain::TimetableFinished()
5033  {
5034  if((ActionVectorEntryPtr == TrainDataEntryPtr->ActionVector.end()) || (ActionVectorEntryPtr->FormatType == Repeat))//past all actions
5035  {
5036  return true;
5037  }
5038  return false;
5039  }
5040 */
5041 // ---------------------------------------------------------------------------
5042 
5043 AnsiString TTrain::GetTrainHeadCode(int Caller)
5044 
5045 {
5046  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainHeadCode" + "," + HeadCode);
5047  AnsiString RepeatHeadCode = TrainController->GetRepeatHeadCode(0, HeadCode, RepeatNumber, IncrementalDigits);
5048 
5049  Utilities->CallLogPop(1452);
5050  return(RepeatHeadCode);
5051 }
5052 
5053 // ---------------------------------------------------------------------------
5054 
5055 TDateTime TTrain::GetTrainTime(int Caller, TDateTime Time)
5056 {
5057  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetTrainTime," + Utilities->Format96HHMMSS(Time));
5058  TDateTime RepeatTime = TrainController->GetRepeatTime(1, Time, RepeatNumber, IncrementalMinutes);
5059 
5060  Utilities->CallLogPop(1453);
5061  return(RepeatTime);
5062 }
5063 
5064 // ---------------------------------------------------------------------------
5065 
5066 bool TTrain::IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
5067 {
5068  // Used to check for a stopped adjacent train for use in PopUp menu //new at v2.4.0
5069  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsThereAnAdjacentTrain" + "," + HeadCode);
5070  // check if there's a stopped adjacent train, if there is but not under sig control give a message in calling function
5071  // first check that train is fully on the railway
5072  bool FrontValid = false, RearValid = false;
5073  TTrackElement FrontAdjacentTrackElement, RearAdjacentTrackElement;
5074 
5075  if((LeadElement == -1) || (MidElement == -1))
5076  {
5077  TrainToBeJoinedBy = NULL;
5078  Utilities->CallLogPop(2131);
5079  return(false);
5080  }
5082  {
5083  FrontAdjacentTrackElement = Track->TrackElementAt(965, (Track->TrackElementAt(966, LeadElement).Conn[LeadExitPos]));
5084  FrontValid = true;
5085  }
5087  {
5088  RearAdjacentTrackElement = Track->TrackElementAt(968, (Track->TrackElementAt(969, MidElement).Conn[MidEntryPos]));
5089  RearValid = true;
5090  }
5091  int TrainToBeJoinedByID = -1;
5092 
5093  // first check if on a 2-track element & select correct ID number if so
5094  if(FrontValid)
5095  {
5096  if(FrontAdjacentTrackElement.TrackType == Bridge)
5097  {
5099  {
5100  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5101  }
5102  else
5103  {
5104  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5105  }
5106  }
5107  else
5108  {
5109  TrainToBeJoinedByID = FrontAdjacentTrackElement.TrainIDOnElement;
5110  }
5111  }
5112  if((TrainToBeJoinedByID < 0) && RearValid)
5113  {
5114  // first check if on a 2-track element & select correct ID number if so
5115  if(RearAdjacentTrackElement.TrackType == Bridge)
5116  {
5118  {
5119  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos23;
5120  }
5121  else
5122  {
5123  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnBridgeTrackPos01;
5124  }
5125  }
5126  else
5127  {
5128  TrainToBeJoinedByID = RearAdjacentTrackElement.TrainIDOnElement;
5129  }
5130  }
5131  if(TrainToBeJoinedByID < 0) // no adjacent train
5132  {
5133  TrainToBeJoinedBy = NULL;
5134  Utilities->CallLogPop(2132);
5135  return(false);
5136  }
5137  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(44, TrainToBeJoinedByID));
5138  if(!TrainToBeJoinedBy->Stopped())
5139  {
5140  TrainToBeJoinedBy = NULL;
5141  Utilities->CallLogPop(2133);
5142  return(false);
5143  }
5144  Utilities->CallLogPop(2134);
5145  return(true);
5146 }
5147 
5148 // ---------------------------------------------------------------------------
5149 
5150 void TTrain::LogAction(int Caller, AnsiString OwnHeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName,
5151  TDateTime TimetableNonRepeatTime, bool Warning)
5152 /*
5153  Time = timetable time, the time adjustments for repeat trains is carried out internally
5154  Not all messages need this, if not needed a dummy value is required but not used
5155 
5156  Arrive: 06:05:40: 2F46 arrived at Old Street 1 minute late
5157  Pass: 06:05:40: 2F46 passed Old Street 1 minute late
5158  Terminate: 06:05:40: 2F46 terminated at Old Street 1 minute late
5159  //NB for Frh just give terminated message but without event time - don't use this function
5160  Depart: 06:05:15: 3F43 departed from Essex Road 2 minutes late
5161  Create: 06:05:40: 2F46 created at Old Street 1 minute late
5162  Enter: 06:05:40: 2F46 entered railway at Old Street 1 minute late
5163  Leave: 06:05:40: 2F46 left railway at 57-N4 1 minute late
5164  FrontSplit: 06:05:40: 2F46 split from front to 3D54 at Old Street 1 minute late
5165  RearSplit: 06:05:40: 2F46 split from rear to 3D54 at Old Street 1 minute late
5166  JoinedByOther: 06:05:40: 2F46 joined by 3D54 at Old Street 1 minute late
5167  ChangeDirection: 06:05:40: 2F46 changed direction at Old Street 1 minute late
5168  NewService: 06:05:40: 2F46 became new service 3D54 at Old Street 1 minute late
5169  TakeManualControl: 06:05:40: 2F46 taken under signaller control at Old Street
5170  RestoreTimetableControl: 06:05:40: 2F46 restored to timetable control at Old Street
5171  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO CRASH at Old Street
5172  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY DUE TO DERAILMENT at Old Street
5173  RemoveTrain: 06:05:40: 2F46 REMOVED FROM RAILWAY at Old Street
5174  SignallerMoveForwards 06:05:40: 2F46 received signaller authority to proceed
5175  SignallerChangeDirection 06:05:40: 2F46 changed direction under signaller control at Old Street
5176  SignallerPassRedSignal 06:05:40: 2F46 received signaller authority to pass red signal
5177  SignallerJoin 06:05:40: 2F46 joined under signaller control by 3D54 at Old Street //new at v2.4.0
5178  TrainFailure 06:05:40: 2F46 suffered an onboard power failure at Old Street //new at v2.4.0
5179  RepairFailedTrain 06:05:40: 2F46 failure repaired at Old Street //new at v2.4.0
5180  SignallerControlStop 06:05:40: 2F46 received signaller instruction to stop
5181  SignallerStop 06:05:40: 2F46 stopped on signaller command
5182  SignallerLeave: 06:05:40: 2F46 left railway under signaller control at 57-N4
5183  SignallerStepForward: 06:05:40: 2F46 received signaller authority to step forward
5184 */{
5185  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogAction," + OwnHeadCode + "," + OtherHeadCode + "," +
5186  AnsiString(ActionType) + "," + LocationName + "," + HeadCode);
5187  AnsiString BaseLog = "", WarningBaseLog = "", PerfLog = "", ActionLog = "";
5188  int IntMinsLate = 0;
5189 
5190  // need to set it in case MinsLate == 0, since it isn't tested for that
5191  if(ActionType == Arrive)
5192  {
5193  ActionLog = " arrived at ";
5194  }
5195  if(ActionType == Terminate)
5196  {
5197  if(TerminatedMessageSent) // to avoid it being sent twice
5198  {
5199  Utilities->CallLogPop(1104);
5200  return;
5201  }
5202  ActionLog = " terminated at ";
5203  TerminatedMessageSent = true;
5204  }
5205  if(ActionType == Depart)
5206  {
5207  ActionLog = " departed from ";
5208  }
5209  if(ActionType == Pass)
5210  {
5211  ActionLog = " passed ";
5212  }
5213  if(ActionType == Create)
5214  {
5215  ActionLog = " created at ";
5216  }
5217  if(ActionType == Enter)
5218  {
5219  ActionLog = " entered railway at ";
5220  }
5221  if(ActionType == Leave)
5222  {
5223  ActionLog = " left railway at ";
5224  }
5225  if(ActionType == FrontSplit)
5226  {
5227  ActionLog = " split from front to ";
5228  }
5229  if(ActionType == RearSplit)
5230  {
5231  ActionLog = " split from rear to ";
5232  }
5233  if(ActionType == JoinedByOther)
5234  {
5235  ActionLog = " joined by ";
5236  }
5237  if(ActionType == ChangeDirection)
5238  {
5239  ActionLog = " changed direction at ";
5240  }
5241  if(ActionType == NewService)
5242  {
5243  ActionLog = " became new service ";
5244  }
5245  if(ActionType == TakeSignallerControl)
5246  {
5247  ActionLog = " taken under signaller control at ";
5248  }
5249  if(ActionType == RestoreTimetableControl)
5250  {
5251  ActionLog = " restored to timetable control at ";
5252  }
5253  if(ActionType == RemoveTrain)
5254  {
5255  if(Crashed)
5256  {
5257  ActionLog = " REMOVED FROM RAILWAY DUE TO CRASH at ";
5258  }
5259  else if(Derailed)
5260  {
5261  ActionLog = " REMOVED FROM RAILWAY DUE TO DERAILMENT at ";
5262  }
5263  else
5264  {
5265  ActionLog = " REMOVED FROM RAILWAY at ";
5266  }
5267  }
5268  if(ActionType == SignallerMoveForwards)
5269  {
5270  ActionLog = " received signaller authority to proceed";
5271  }
5272  if(ActionType == SignallerStepForward)
5273  {
5274  ActionLog = " received signaller authority to step forward";
5275  }
5276  if(ActionType == SignallerChangeDirection)
5277  {
5278  ActionLog = " changed direction under signaller control at ";
5279  }
5280  if(ActionType == SignallerPassRedSignal)
5281  {
5282  ActionLog = " received signaller authority to pass red signal";
5283  }
5284  if(ActionType == SignallerControlStop)
5285  {
5286  ActionLog = " received signaller instruction to stop";
5287  }
5288  if(ActionType == SignallerStop)
5289  {
5290  ActionLog = " stopped on signaller instruction ";
5291  }
5292  if(ActionType == SignallerJoin)
5293  {
5294  ActionLog = " joined under signaller control by ";
5295  }
5296  if(ActionType == TrainFailure)
5297  {
5298  ActionLog = " suffered an onboard power failure at ";
5299  }
5300  if(ActionType == RepairFailedTrain)
5301  {
5302  ActionLog = " failure repaired at ";
5303  }
5304  if(ActionType == SignallerLeave)
5305  {
5306  ActionLog = " left railway under signaller control at ";
5307  }
5308  if(OtherHeadCode != "")
5309  {
5310  OtherHeadCode += " at ";
5311  }
5312  TDateTime ActualTime = TrainController->TTClockTime;
5313 
5314  if(Warning)
5315  {
5316  BaseLog = Utilities->Format96HHMMSS(ActualTime) + " WARNING: " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5317  WarningBaseLog = HeadCode + ActionLog + OtherHeadCode + LocationName;
5318  }
5319  else
5320  {
5321  BaseLog = Utilities->Format96HHMMSS(ActualTime) + ": " + HeadCode + ActionLog + OtherHeadCode + LocationName;
5322  }
5323  bool TimePerformance = true;
5324 
5325  if((ActionType == TakeSignallerControl) || (ActionType == RestoreTimetableControl) || (ActionType == RemoveTrain) || (ActionType == SignallerMoveForwards)
5326  || (ActionType == SignallerChangeDirection) || (ActionType == SignallerPassRedSignal) || (ActionType == SignallerControlStop) ||
5327  (ActionType == SignallerStop) || (ActionType == SignallerLeave) || (ActionType == SignallerStepForward) || (ActionType == SignallerJoin) ||
5328  (ActionType == TrainFailure) || (ActionType == RepairFailedTrain))
5329  // SignallerJoin & RepairFailedTrain new at v2.4.0
5330  {
5331  TimePerformance = false;
5332  }
5333  if(TimePerformance)
5334  {
5335  double MinsLate = ((double)(ActualTime - GetTrainTime(1, TimetableNonRepeatTime))) * 1440;
5336  MinsDelayed = float(MinsLate);
5337  if(ActionType == Pass) //added at v2.9.2 to prevent time to act increasing suddenly for early pass times then becoming 'NOW' when stops at signal
5338  {
5339  MinsDelayed = 0;
5340  }
5341  // new v2.2.0 for OpActionPanel, can be positive or negative
5342  if(ActionType == Arrive)
5343  {
5345  }
5346  // since train has just arrived this value is the
5347  // recoverable time available at this stop, so reduce MinsDelayed by this amount to prevent it being
5348  // subtracted from later stop recoverable times.
5349  if(MinsLate < 0)
5350  {
5351  IntMinsLate = int(ceil(MinsLate));
5352  }
5353  if(MinsLate > 0)
5354  {
5355  IntMinsLate = int(floor(MinsLate));
5356  }
5357  if(IntMinsLate == 0)
5358  {
5359  PerfLog = " on time";
5360  }
5361  else if(IntMinsLate == 1)
5362  {
5363  PerfLog = " 1 minute late";
5364  }
5365  else if(IntMinsLate == -1)
5366  {
5367  PerfLog = " 1 minute early";
5368  }
5369  else if(IntMinsLate > 1)
5370  {
5371  PerfLog = " " + AnsiString(IntMinsLate) + " minutes late";
5372  }
5373  else if(IntMinsLate < -1)
5374  {
5375  int PosIntMinsLate = -IntMinsLate;
5376  PerfLog = " " + AnsiString(PosIntMinsLate) + " minutes early";
5377  }
5378  if(LocationName.Pos('-') > 0)
5379  {
5380  PerfLog = "," + PerfLog;
5381  // if a position add a comma to separate vertical position number from number of minutes (better appearance)
5382  }
5383  Display->PerformanceLog(0, BaseLog + PerfLog);
5384  }
5385  else
5386  {
5387  Display->PerformanceLog(1, BaseLog);
5388  }
5389  if(Warning)
5390  {
5391  Display->WarningLog(0, WarningBaseLog);
5392  }
5393  // update statistics
5394  if((ActionType == Arrive) && (IntMinsLate == 0))
5395  {
5397  }
5398  else if((ActionType == Arrive) && (IntMinsLate > 0))
5399  {
5401  TrainController->TotLateArrMins += IntMinsLate;
5402  }
5403  else if((ActionType == Arrive) && (IntMinsLate < 0))
5404  {
5406  TrainController->TotEarlyArrMins += abs(IntMinsLate);
5407  }
5408 
5409  else if((ActionType == Pass) && (IntMinsLate == 0))
5410  {
5412  }
5413  else if((ActionType == Pass) && (IntMinsLate > 0))
5414  {
5416  TrainController->TotLatePassMins += IntMinsLate;
5417  }
5418  else if((ActionType == Pass) && (IntMinsLate < 0))
5419  {
5421  TrainController->TotEarlyPassMins += abs(IntMinsLate);
5422  }
5423 
5424  else if((ActionType == Leave) && (IntMinsLate == 0)) //new at v2.9.1 as had been omitted in error earlier
5425  {
5427  }
5428  else if((ActionType == Leave) && (IntMinsLate > 0))
5429  {
5431  TrainController->TotLateExitMins += IntMinsLate;
5432  }
5433  else if((ActionType == Leave) && (IntMinsLate < 0))
5434  {
5436  TrainController->TotEarlyExitMins += abs(IntMinsLate);
5437  }
5438 
5439  else if((ActionType == Depart) && (IntMinsLate == 0)) //can't depart early
5440  {
5442  }
5443  else if((ActionType == Depart) && (IntMinsLate > 0))
5444  {
5446  TrainController->TotLateDepMins += IntMinsLate;
5447  }
5448  Utilities->CallLogPop(968);
5449 }
5450 
5451 // ---------------------------------------------------------------------------
5452 
5453 void TTrain::TrainHasFailed(int Caller)
5454 {
5455  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainHasFailed," + HeadCode);
5456  if(Crashed || Derailed || DerailPending)
5457  {
5458  TrainFailurePending = false;
5459  Utilities->CallLogPop(2135);
5460  return;
5461  }
5462  AnsiString LocName = "";
5463 
5464  if(LeadElement > -1)
5465  {
5467  }
5468  if((LocName == "") && (MidElement > -1))
5469  {
5471  }
5472  if((LocName == "") && LeadElement > -1)
5473  {
5474  LocName = Track->TrackElementAt(974, LeadElement).ElementID;
5475  }
5476  if((LocName == "") && (MidElement > -1))
5477  {
5478  LocName = Track->TrackElementAt(975, MidElement).ElementID;
5479  }
5480  TrainController->StopTTClockMessage(81, HeadCode + " has suffered an onboard power failure at " + LocName);
5481  TrainFailed = true;
5482  TrainFailurePending = false;
5483  CallingOnFlag = false; //added at v2.10.0
5485  PowerAtRail = 0.08;
5486  AValue = sqrt(2 * PowerAtRail / Mass);
5488  // TrainFailed only called when PlotElements properly set to Lead, Mid & Lag elements
5489  if(Stopped())
5490  {
5491  EntrySpeed = 0;
5492  ExitSpeedHalf = 0;
5493  ExitSpeedFull = 0;
5494  MaxExitSpeed = 0;
5495  BrakeRate = 0;
5496  StoppedWithoutPower = true;
5497  }
5499  LogAction(33, HeadCode, "", TrainFailure, LocName, TDateTime(0), true);
5500  // true for warning, TDateTime not used
5501  Utilities->CallLogPop(2136);
5502 }
5503 
5504 // ---------------------------------------------------------------------------
5505 
5506 void TTrain::FrontTrainSplit(int Caller)
5507 {
5508 /*
5509  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5510  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5511  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5512  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5513 */
5514  TrainController->LogEvent("" + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5515  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FrontTrainSplit" + "," + HeadCode);
5516  if(PowerAtRail < 1)
5517  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5518  {
5520  {
5521  TrainController->StopTTClockMessage(82, HeadCode + ": A train without power can't split");
5522  }
5524  Utilities->CallLogPop(2137);
5525  return;
5526  }
5527  AnsiString LocationName = Track->TrackElementAt(555, LeadElement).ActiveTrackElementName;
5528 
5529  if(LocationName == "")
5530  {
5531  LocationName = Track->TrackElementAt(837, MidElement).ActiveTrackElementName;
5532  }
5533  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5534  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5535  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5537 
5538  // determine all positions & exits
5539  if(LocationName != "")
5540  {
5541  // if message given only call at ~5 sec intervals
5543  {
5544  FirstNamedElementPos = LeadElement;
5545  if(!Track->ThisNamedLocationLongEnoughForSplit(0, LocationName, FirstNamedElementPos,
5546  // check if possible with LeadElement as First
5547  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5548  {
5549  FirstNamedElementPos = MidElement;
5550  if(!Track->ThisNamedLocationLongEnoughForSplit(1, LocationName, FirstNamedElementPos,
5551  // if not then accept second if possible (though if Lead no good hard to see how Mid could work, but leave in)
5552  SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos))
5553  {
5555  {
5556  TrainController->LogActionError(6, HeadCode, "", FailLocTooShort, LocationName);
5558  }
5559  Utilities->CallLogPop(1009);
5560  return;
5561  }
5562  }
5563  else
5564  {
5565  // if first is possible then check if all 4 positions at location, and if not try the second
5566  int LeadPosA = FirstNamedElementPos;
5567  int LeadPosB = FirstNamedLinkedElementPos;
5568  int LeadPosC = SecondNamedElementPos;
5569  int LeadPosD = SecondNamedLinkedElementPos;
5570  // count number of positions that are at the location
5571  int LeadNumAtLoc = 0;
5572  if(Track->TrackElementAt(758, LeadPosA).ActiveTrackElementName == LocationName)
5573  {
5574  LeadNumAtLoc++;
5575  }
5576  if(Track->TrackElementAt(759, LeadPosB).ActiveTrackElementName == LocationName)
5577  {
5578  LeadNumAtLoc++;
5579  }
5580  if(Track->TrackElementAt(760, LeadPosC).ActiveTrackElementName == LocationName)
5581  {
5582  LeadNumAtLoc++;
5583  }
5584  if(Track->TrackElementAt(761, LeadPosD).ActiveTrackElementName == LocationName)
5585  {
5586  LeadNumAtLoc++;
5587  }
5588  if(LeadNumAtLoc < 4)
5589  {
5590  FirstNamedElementPos = MidElement;
5591  if(!Track->ThisNamedLocationLongEnoughForSplit(4, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5592  SecondNamedLinkedElementPos)) // restore originals
5593  {
5594  FirstNamedElementPos = LeadPosA;
5595  FirstNamedLinkedElementPos = LeadPosB;
5596  SecondNamedElementPos = LeadPosC;
5597  SecondNamedLinkedElementPos = LeadPosD;
5598  }
5599  else // count number at location
5600  {
5601  int MidNumAtLoc = 0;
5602  if(Track->TrackElementAt(762, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5603  {
5604  MidNumAtLoc++;
5605  }
5606  if(Track->TrackElementAt(763, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5607  {
5608  MidNumAtLoc++;
5609  }
5610  if(Track->TrackElementAt(764, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5611  {
5612  MidNumAtLoc++;
5613  }
5614  if(Track->TrackElementAt(765, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5615  {
5616  MidNumAtLoc++;
5617  }
5618  if(LeadNumAtLoc > MidNumAtLoc)
5619  // change back, else keep new values
5620  {
5621  FirstNamedElementPos = LeadPosA;
5622  FirstNamedLinkedElementPos = LeadPosB;
5623  SecondNamedElementPos = LeadPosC;
5624  SecondNamedLinkedElementPos = LeadPosD;
5625  }
5626  }
5627  }
5628  }
5629  }
5630  else
5631  {
5632  Utilities->CallLogPop(1791);
5633  return;
5634  }
5635  }
5636  else
5637  {
5638  throw Exception("Error - LocationName not set in FrontTrainSplit");
5639  }
5640  // set front & rear train parameters
5641  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5642  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5643  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5644  if(LeadElement == FirstNamedElementPos)
5645  {
5646  if(MidElement == SecondNamedElementPos)
5647  {
5648  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5649  FrontTrainRearPosition = FirstNamedElementPos;
5650  RearTrainFrontPosition = SecondNamedElementPos;
5651  RearTrainRearPosition = SecondNamedLinkedElementPos;
5652  }
5653  else // MidElement must == FirstNamedLinkedElementPos
5654  {
5655  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5656  FrontTrainRearPosition = SecondNamedElementPos;
5657  RearTrainFrontPosition = FirstNamedElementPos;
5658  RearTrainRearPosition = FirstNamedLinkedElementPos;
5659  }
5660  }
5661  else // MidElement == FirstNamedElementPos
5662  {
5663  if(LeadElement == SecondNamedElementPos)
5664  {
5665  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5666  FrontTrainRearPosition = SecondNamedElementPos;
5667  RearTrainFrontPosition = FirstNamedElementPos;
5668  RearTrainRearPosition = FirstNamedLinkedElementPos;
5669  }
5670  else // LeadElement must == FirstNamedLinkedElementPos
5671  {
5672  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5673  FrontTrainRearPosition = FirstNamedElementPos;
5674  RearTrainFrontPosition = SecondNamedElementPos;
5675  RearTrainRearPosition = SecondNamedLinkedElementPos;
5676  }
5677  }
5678  RearTrainExitPos = -1;
5679  for(int x = 0; x < 4; x++)
5680  {
5681  if(Track->TrackElementAt(584, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5682  {
5683  RearTrainExitPos = x;
5684  break;
5685  }
5686  }
5687  if(RearTrainExitPos == -1)
5688  {
5689  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in FrontTrainSplit");
5690  }
5691  FrontTrainExitPos = -1;
5692  for(int x = 0; x < 4; x++)
5693  {
5694  if(Track->TrackElementAt(585, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
5695  {
5696  FrontTrainExitPos = x;
5697  break;
5698  }
5699  }
5700  if(FrontTrainExitPos == -1)
5701  {
5702  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in FrontTrainSplit");
5703  }
5704  // check no train (apart from self) on any of the 4 elements & fail if so
5705  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
5706  TTrackElement RearMostElement = Track->TrackElementAt(574, RearTrainRearPosition);
5707 
5708  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
5709  {
5710  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
5711  }
5712  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
5713  {
5714  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
5715  }
5716  else
5717  {
5718  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
5719  }
5720  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
5721  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(575, RearTrainFrontPosition).TrainIDOnElement;
5722  // can't be a bridge
5723  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(576, FrontTrainRearPosition).TrainIDOnElement;
5724  // can't be a bridge
5725  // FrontTrainFrontPosition = Track->TrackElementAt(578,FrontTrainRearPosition).Conn[FrontTrainExitPos];
5726  TTrackElement FrontMostElement = Track->TrackElementAt(577, FrontTrainFrontPosition);
5727 
5728  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
5729  {
5730  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
5731  }
5732  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
5733  {
5734  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
5735  }
5736  else
5737  {
5738  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
5739  }
5740  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
5741  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
5742  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
5743  {
5745  {
5748  }
5749  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
5750  Utilities->CallLogPop(1010);
5751  return;
5752  }
5754  {
5756  }
5757  // reposition existing rear train, need to do this first for 2 reasons - 1) will likely be in the way of the new front train, and 2)
5758  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
5759  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
5760  // variable as it is needed for setting up the new train
5761  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
5762 
5763  UnplotTrain(0);
5764  StartSpeed = 0;
5765  RearStartElement = RearTrainRearPosition;
5766  RearStartExitPos = RearTrainExitPos;
5767  StoppedAtLocation = true;
5768  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
5769  {
5770  StoppedWithoutPower = true;
5771  }
5772  PlotStartPosition(3);
5777 
5778  Mass = Mass / 2;
5779  MaxBrakeRate = MaxBrakeRate / 2;
5780  PowerAtRail = PowerAtRail / 2;
5781  AValue = sqrt(2 * PowerAtRail / Mass);
5782  // shouldn't change but include in case not set earlier
5783 
5784  // create new front train
5785 /*
5786  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
5787  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
5788  int RepeatNumber, int IncrementalMinutes, int SignallerSpeed)
5789 */
5790  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
5791  TActionEventType EventType = NoEvent;
5792 
5793  if(!TrainController->AddTrain(0, FrontTrainRearPosition, FrontTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
5794  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
5795  // false for SignallerControl
5796  {
5797  Utilities->CallLogPop(1721); // EventType not used here
5798  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
5799  // another train, in which case a message will have been sent to the perf log, also might well clear later
5800  // when other train moves away
5801  return;
5802  }
5803  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
5804  // see mods in UpdateTrain for v1.3.2
5805  TrainController->TrainAdded = true;
5806 
5807  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
5808 
5809  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
5810  TTOD.RunningEntry = Running;
5811  Utilities->CallLogPop(998);
5812 }
5813 
5814 // ---------------------------------------------------------------------------
5815 
5816 void TTrain::RearTrainSplit(int Caller)
5817 {
5818 /*
5819  Split logic is:- at least one of 4 final train positions must overlap with one of original train positions, & final 4 positions
5820  will maximise the number at the location. Note that this function isn't sophisticated enough to account for trains already at the
5821  location in determining the 4 positions, and will give a failure message if a train obstructs any of the 4 positions. In these
5822  circumstances the other train will need to be moved sufficiently away to release all 4 positions, then the train will split.
5823 */
5824  TrainController->LogEvent("" + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5825  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RearTrainSplit" + "," + HeadCode);
5826  if(PowerAtRail < 1)
5827  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can split when power restored
5828  {
5830  {
5831  TrainController->StopTTClockMessage(83, HeadCode + ": A train without power can't split");
5832  }
5834  Utilities->CallLogPop(2138);
5835  return;
5836  }
5837  AnsiString LocationName = Track->TrackElementAt(587, LeadElement).ActiveTrackElementName;
5838 
5839  if(LocationName == "")
5840  {
5841  LocationName = Track->TrackElementAt(838, MidElement).ActiveTrackElementName;
5842  }
5843  int FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos, SecondNamedLinkedElementPos;
5844  int RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos;
5845  int FrontTrainRearPosition, FrontTrainFrontPosition, FrontTrainExitPos;
5847 
5848  // determine all positions & exits
5849  if(LocationName != "")
5850  {
5851  // if message given only call at ~5 sec intervals
5853  {
5854  FirstNamedElementPos = LeadElement;
5855  if(!Track->ThisNamedLocationLongEnoughForSplit(2, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5856  SecondNamedLinkedElementPos))
5857  {
5858  FirstNamedElementPos = MidElement;
5859  if(!Track->ThisNamedLocationLongEnoughForSplit(3, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5860  SecondNamedLinkedElementPos))
5861  {
5863  {
5864  TrainController->LogActionError(9, HeadCode, "", FailLocTooShort, LocationName);
5866  }
5867  Utilities->CallLogPop(1013);
5868  return;
5869  }
5870  }
5871  else
5872  {
5873  // if first is possible then check if all 4 positions at location, and if not try the second
5874  int LeadPosA = FirstNamedElementPos;
5875  int LeadPosB = FirstNamedLinkedElementPos;
5876  int LeadPosC = SecondNamedElementPos;
5877  int LeadPosD = SecondNamedLinkedElementPos;
5878  // count number of positions that are at the location
5879  int LeadNumAtLoc = 0;
5880  if(Track->TrackElementAt(767, LeadPosA).ActiveTrackElementName == LocationName)
5881  {
5882  LeadNumAtLoc++;
5883  }
5884  if(Track->TrackElementAt(768, LeadPosB).ActiveTrackElementName == LocationName)
5885  {
5886  LeadNumAtLoc++;
5887  }
5888  if(Track->TrackElementAt(769, LeadPosC).ActiveTrackElementName == LocationName)
5889  {
5890  LeadNumAtLoc++;
5891  }
5892  if(Track->TrackElementAt(770, LeadPosD).ActiveTrackElementName == LocationName)
5893  {
5894  LeadNumAtLoc++;
5895  }
5896  if(LeadNumAtLoc < 4)
5897  {
5898  FirstNamedElementPos = MidElement;
5899  if(!Track->ThisNamedLocationLongEnoughForSplit(5, LocationName, FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos,
5900  SecondNamedLinkedElementPos)) // restore originals
5901  {
5902  FirstNamedElementPos = LeadPosA;
5903  FirstNamedLinkedElementPos = LeadPosB;
5904  SecondNamedElementPos = LeadPosC;
5905  SecondNamedLinkedElementPos = LeadPosD;
5906  }
5907  else // count number at location
5908  {
5909  int MidNumAtLoc = 0;
5910  if(Track->TrackElementAt(771, FirstNamedElementPos).ActiveTrackElementName == LocationName)
5911  {
5912  MidNumAtLoc++;
5913  }
5914  if(Track->TrackElementAt(772, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5915  {
5916  MidNumAtLoc++;
5917  }
5918  if(Track->TrackElementAt(773, FirstNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5919  {
5920  MidNumAtLoc++;
5921  }
5922  if(Track->TrackElementAt(774, SecondNamedLinkedElementPos).ActiveTrackElementName == LocationName)
5923  {
5924  MidNumAtLoc++;
5925  }
5926  if(LeadNumAtLoc > MidNumAtLoc)
5927  // change back, else keep new values
5928  {
5929  FirstNamedElementPos = LeadPosA;
5930  FirstNamedLinkedElementPos = LeadPosB;
5931  SecondNamedElementPos = LeadPosC;
5932  SecondNamedLinkedElementPos = LeadPosD;
5933  }
5934  }
5935  }
5936  }
5937  }
5938  else
5939  {
5940  Utilities->CallLogPop(1792);
5941  return;
5942  }
5943  }
5944  else
5945  {
5946  throw Exception("Error - LocationName not set in RearTrainSplit");
5947  }
5948  // set front & rear train parameters
5949  // need RearTrainRearPosition, RearTrainFrontPosition, RearTrainExitPos, FrontTrainRearPosition, FrontTrainFrontPosition & FrontTrainExitPos;
5950  // have LeadElement & MidElement of train defining its direction, & one or other on FirstNamedElementPos
5951  // have FirstNamedElementPos, SecondNamedElementPos, FirstNamedLinkedElementPos & SecondNamedLinkedElementPos from ThisNamedLocationLongEnoughForSplit.
5952  if(LeadElement == FirstNamedElementPos)
5953  {
5954  if(MidElement == SecondNamedElementPos)
5955  {
5956  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5957  FrontTrainRearPosition = FirstNamedElementPos;
5958  RearTrainFrontPosition = SecondNamedElementPos;
5959  RearTrainRearPosition = SecondNamedLinkedElementPos;
5960  }
5961  else // MidElement must == FirstNamedLinkedElementPos
5962  {
5963  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5964  FrontTrainRearPosition = SecondNamedElementPos;
5965  RearTrainFrontPosition = FirstNamedElementPos;
5966  RearTrainRearPosition = FirstNamedLinkedElementPos;
5967  }
5968  }
5969  else // MidElement == FirstNamedElementPos
5970  {
5971  if(LeadElement == SecondNamedElementPos)
5972  {
5973  FrontTrainFrontPosition = SecondNamedLinkedElementPos;
5974  FrontTrainRearPosition = SecondNamedElementPos;
5975  RearTrainFrontPosition = FirstNamedElementPos;
5976  RearTrainRearPosition = FirstNamedLinkedElementPos;
5977  }
5978  else // LeadElement must == FirstNamedLinkedElementPos
5979  {
5980  FrontTrainFrontPosition = FirstNamedLinkedElementPos;
5981  FrontTrainRearPosition = FirstNamedElementPos;
5982  RearTrainFrontPosition = SecondNamedElementPos;
5983  RearTrainRearPosition = SecondNamedLinkedElementPos;
5984  }
5985  }
5986  RearTrainExitPos = -1;
5987  for(int x = 0; x < 4; x++)
5988  {
5989  if(Track->TrackElementAt(588, RearTrainRearPosition).Conn[x] == RearTrainFrontPosition)
5990  {
5991  RearTrainExitPos = x;
5992  break;
5993  }
5994  }
5995  if(RearTrainExitPos == -1)
5996  {
5997  throw Exception("Error - RearTrainRearPosition not linked to RearTrainFrontPosition in RearTrainSplit");
5998  }
5999  FrontTrainExitPos = -1;
6000  for(int x = 0; x < 4; x++)
6001  {
6002  if(Track->TrackElementAt(589, FrontTrainRearPosition).Conn[x] == FrontTrainFrontPosition)
6003  {
6004  FrontTrainExitPos = x;
6005  break;
6006  }
6007  }
6008  if(FrontTrainExitPos == -1)
6009  {
6010  throw Exception("Error - FrontTrainRearPosition not linked to FrontTrainFrontPosition in RearTrainSplit");
6011  }
6012  // check no train (apart from self) on any of the 4 elements & fail if so
6013  int TrainIDOnRearOfRearTrain, TrainIDOnFrontOfRearTrain, TrainIDOnRearOfFrontTrain, TrainIDOnFrontOfFrontTrain;
6014  TTrackElement RearMostElement = Track->TrackElementAt(590, RearTrainRearPosition);
6015 
6016  if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos > 1))
6017  {
6018  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos23;
6019  }
6020  else if((RearMostElement.TrackType == Bridge) && (RearTrainExitPos < 2))
6021  {
6022  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnBridgeTrackPos01;
6023  }
6024  else
6025  {
6026  TrainIDOnRearOfRearTrain = RearMostElement.TrainIDOnElement;
6027  }
6028  // RearTrainFrontPosition = RearMostElement.Conn[RearTrainExitPos];
6029  TrainIDOnFrontOfRearTrain = Track->TrackElementAt(591, RearTrainFrontPosition).TrainIDOnElement;
6030  // can't be a bridge
6031  TrainIDOnRearOfFrontTrain = Track->TrackElementAt(592, FrontTrainRearPosition).TrainIDOnElement;
6032  // can't be a bridge
6033  // FrontTrainFrontPosition = Track->TrackElementAt(593,FrontTrainRearPosition).Conn[FrontTrainExitPos];
6034  TTrackElement FrontMostElement = Track->TrackElementAt(594, FrontTrainFrontPosition);
6035 
6036  if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos > 1))
6037  {
6038  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos23;
6039  }
6040  else if((FrontMostElement.TrackType == Bridge) && (FrontTrainExitPos < 2))
6041  {
6042  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnBridgeTrackPos01;
6043  }
6044  else
6045  {
6046  TrainIDOnFrontOfFrontTrain = FrontMostElement.TrainIDOnElement;
6047  }
6048  if(((TrainIDOnRearOfRearTrain > -1) && (TrainIDOnRearOfRearTrain != TrainID)) || ((TrainIDOnFrontOfRearTrain > -1) && (TrainIDOnFrontOfRearTrain != TrainID)
6049  ) || ((TrainIDOnRearOfFrontTrain > -1) && (TrainIDOnRearOfFrontTrain != TrainID)) ||
6050  ((TrainIDOnFrontOfFrontTrain > -1) && (TrainIDOnFrontOfFrontTrain != TrainID)))
6051  {
6053  {
6056  }
6057  // don't advance ActionVectorEntryPtr as need to keep trying, other train may move off eventually
6058  Utilities->CallLogPop(1014);
6059  return;
6060  }
6062  {
6064  }
6065  // reposition existing front train, need to do this first for 2 reasons - 1) will likely be in the way of the new rear train, and 2)
6066  // the new train will likely cause a reallocation of the TrainVector, and if so the reference to the existing train will be invalidated.
6067  // Hence deal with existing train while it references a valid entry in the vector, but retain the Old ActionVectorEntryPtr in a separate
6068  // variable as it is needed for setting up the new train
6069  TActionVectorEntry *OldActionVectorEntryPtr = ActionVectorEntryPtr;
6070 
6071  UnplotTrain(1);
6072  StartSpeed = 0;
6073  RearStartElement = FrontTrainRearPosition;
6074  RearStartExitPos = FrontTrainExitPos;
6075  StoppedAtLocation = true;
6076  if((PowerAtRail < 1) && EntrySpeed < 1) // added at v2.4.0
6077  {
6078  StoppedWithoutPower = true;
6079  }
6080  PlotStartPosition(4);
6085  Mass = Mass / 2;
6086  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).Mass = Mass;
6087  MaxBrakeRate = MaxBrakeRate / 2;
6088  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).MaxBrakeRate = MaxBrakeRate;
6089  PowerAtRail = PowerAtRail / 2;
6090  // TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).PowerAtRail = PowerAtRail;
6091  AValue = sqrt(2 * PowerAtRail / Mass);
6092  // shouldn't change but include in case not set earlier
6093 
6094  // create new rear train
6095 /*
6096  TrainController::AddTrain(int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass,
6097  double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr,
6098  int RepeatNumber, int IncrementalMinutes)
6099 */
6100  // same Mass, MaxBrakeRate & PowerAtRail as this train's halved values, and same MaxRunningSpeed as this train
6101  TActionEventType EventType = NoEvent;
6102 
6103  if(!TrainController->AddTrain(1, RearTrainRearPosition, RearTrainFrontPosition, OtherHeadCode, 0, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail,
6104  "Timetable", OldActionVectorEntryPtr->LinkedTrainEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerMaxSpeed, false, EventType))
6105  // false for SignallerControl
6106  {
6107  Utilities->CallLogPop(1722); // EventType not used here
6108  // if fails either a throw will have been sent in AddTrain or start position failed prob because of
6109  // another train, in which case a message will have been sent to the perf log, also might well clear later
6110  // when other train moves away
6111  return;
6112  }
6113  // Note data in 'this' now probably invalid as there has been a new addition to the TrainVector, so the train is likely to have a new address, hence make no more changes for the current train
6114  // see mods in UpdateTrain for v1.3.2
6115  TrainController->TrainAdded = true;
6116  TTrainOperatingData &TTOD = OldActionVectorEntryPtr->LinkedTrainEntryPtr->TrainOperatingDataVector.at(RepeatNumber); // this is for the newly created train
6117 
6118  TTOD.TrainID = TrainController->TrainVector.back().TrainID;
6119  TTOD.RunningEntry = Running;
6120  Utilities->CallLogPop(1015);
6121 }
6122 
6123 // ---------------------------------------------------------------------------
6124 
6125 void TTrain::FinishJoin(int Caller)
6126 {
6127  if(FinishJoinLogSent == false)
6128  {
6129  TrainController->LogEvent("" + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6130  FinishJoinLogSent = true; // so don't keep logging this event
6131  // don't need to reset it to false after the event as the train is deleted
6132  }
6133  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishJoin" + "," + HeadCode);
6134  if(TrainFailed)
6135  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when repaired) Can FinishJoin if zero power & not failed, as for empty stock
6136  {
6138  {
6139  TrainController->StopTTClockMessage(84, HeadCode + ": A failed train can't join another under timetable control");
6140  }
6142  Utilities->CallLogPop(2139);
6143  return;
6144  }
6145  if(TrainGone)
6146  // this means that the train has already joined the other & is awaiting deletion by TrainController
6147  // without this the 'waiting' message can be given since the other train's ActionVectorEntryPtr has moved
6148  // on from jbo & TrainToJoinIsAdjacent returns false
6149  {
6150  Utilities->CallLogPop(1035);
6151  return;
6152  }
6153  TTrain *TrainToJoin;
6155 
6156  if(!TrainToJoinIsAdjacent(0, TrainToJoin))
6157  {
6159  {
6160  // Display->PerformanceLog(2, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to join " + JBOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6163  }
6164  Utilities->CallLogPop(1030);
6165  return; // keep this here in case need to add code before final return
6166  }
6167  // no need to clear error report flag here, cleared in jbo function
6168  // No need to set TimetableFinished, done in jbo function
6169  Utilities->CallLogPop(1031);
6170 }
6171 
6172 // ---------------------------------------------------------------------------
6173 
6174 void TTrain::JoinedBy(int Caller)
6175 {
6176  TrainController->LogEvent("" + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6177  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",JoinedBy" + "," + HeadCode);
6178  if(PowerAtRail < 1)
6179  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can join when power restored)
6180  {
6182  {
6183  TrainController->StopTTClockMessage(85, HeadCode + ": A train without power can't be joined by another under timetable control");
6184  }
6186  Utilities->CallLogPop(2140);
6187  return;
6188  }
6189  TTrain *TrainToBeJoinedBy;
6191 
6192  if(!TrainToBeJoinedByIsAdjacent(0, TrainToBeJoinedBy))
6193  {
6195  {
6196  // Display->PerformanceLog(3, TrainController->TTClockTime.FormatString("hh:nn:ss") + ": " + HeadCode + " waiting to be joined by " + FJOHeadCode + " at " + ActionVectorEntryPtr->LocationName);
6199  }
6200  LastActionDelayFlag = true;
6201  // need to update LastActionTime if this train first to arrive as need 30s after
6202  // both adjacent before the join
6203  Utilities->CallLogPop(1032);
6204  return;
6205  }
6206  // here when other train is adjacent
6208  {
6210  // need to update this as need 30s after both adjacent before the join
6211  LastActionDelayFlag = false;
6212  Utilities->CallLogPop(1033);
6213  return;
6214  }
6215  // here when other train is adjacent & 30 secs elapsed since both adjacent
6216 
6217  // set new values for mass etc
6218  if(MaxRunningSpeed > TrainToBeJoinedBy->MaxRunningSpeed)
6219  {
6220  MaxRunningSpeed = TrainToBeJoinedBy->MaxRunningSpeed;
6221  }
6222  double OtherBrakeForce = TrainToBeJoinedBy->MaxBrakeRate * TrainToBeJoinedBy->Mass;
6223  double OwnBrakeForce = MaxBrakeRate * Mass;
6224  double CombinedBrakeRate = (OtherBrakeForce + OwnBrakeForce) / (TrainToBeJoinedBy->Mass + Mass);
6225 
6226  Mass += TrainToBeJoinedBy->Mass;
6227  MaxBrakeRate = CombinedBrakeRate;
6228  PowerAtRail += TrainToBeJoinedBy->PowerAtRail;
6229  AValue = sqrt(2 * PowerAtRail / Mass);
6230 
6232  TrainToBeJoinedBy->TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
6233  TrainToBeJoinedBy->TimetableFinished = true;
6234  TrainToBeJoinedBy->TrainGone = true;
6235  // this will cause other train to be deleted
6236  TrainToBeJoinedBy->JoinedOtherTrainFlag = true;
6240  Utilities->CallLogPop(1034);
6241 }
6242 
6243 // ---------------------------------------------------------------------------
6244 
6245 void TTrain::ChangeTrainDirection(int Caller, bool NoLogFlag)
6246 {
6247  TrainController->LogEvent("" + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6248  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ChangeTrainDirection" + "," + HeadCode);
6249  if(PowerAtRail < 1)
6250  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can change direction when power restored)
6251  {
6253  {
6254  TrainController->StopTTClockMessage(86, HeadCode + ": A train without power can't change direction under timetable control");
6255  }
6256  ZeroPowerNoCDTMessage = true;
6257  Utilities->CallLogPop(2141);
6258  return;
6259  }
6260  TColor TempColour = BackgroundColour;
6261 
6262  UnplotTrain(2);
6265  StartSpeed = 0;
6266  StoppedAtLocation = true;
6267  PlotStartPosition(1);
6268  PlotTrainWithNewBackgroundColour(27, TempColour, Display);
6269  // plot same as was - should always be pale green
6270  if(!NoLogFlag)
6271  {
6274  }
6276 
6277  //now erase a stub route if there is one, added at v2.5.1
6278  //first element of route is now immediately behind the train (i.e. next to MidElement)
6279  if(MidEntryPos >= 0)
6280  {
6281  TTrackElement MidTrackElement = Track->TrackElementAt(996, MidElement);
6282  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6283  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6284  int RouteNumber = -1;
6285  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(34, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6286  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6287  {
6288  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(28, RouteNumber);
6289  TTrackElement TE = Track->TrackElementAt(997, FirstRouteElementVecPos);
6290  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6291  {
6292  bool FirstPass = true; //added at v2.8.0
6293  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a pref dir route extends to another signal
6294  {
6295  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(247, 0); //these will change at each element removal because OR is a reference to the real route
6296  int TVPos2 = PDE.GetTrackVectorPosition();
6297  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6298  {
6299  break;
6300  }
6301  TTrackElement TE2 = Track->TrackElementAt(998, TVPos2);
6303  {
6304  AllRoutes->RemoveRouteElement(22, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6305  }
6306  else
6307  {
6308  break;
6309  }
6310  FirstPass = false;
6311  }
6312  AllRoutes->RebuildRailwayFlag = true;
6313  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6314  }
6315  }
6316  }
6317  Utilities->CallLogPop(1012);
6318 }
6319 
6320 // ---------------------------------------------------------------------------
6321 
6322 void TTrain::NewTrainService(int Caller, bool NoLogFlag) //, bool NoLogFlag added at v2.12.0 for new service tt skips
6323 // change to new train, give new service message
6324 //same RepeatNumber used for the new service
6325 {
6326  TrainController->LogEvent("" + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6327  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewTrainService" + "," + HeadCode);
6328  if(PowerAtRail < 1)
6329  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can form new service when power restored)
6330  {
6332  {
6333  TrainController->StopTTClockMessage(87, HeadCode + ": A train without power can't form a new service");
6334  }
6336  Utilities->CallLogPop(2142);
6337  return;
6338  }
6340 
6341  if(!NoLogFlag)
6342  {
6344  }
6345  UnplotTrain(3);
6348  StartSpeed = 0;
6353  HeadCode = NewHeadCode;
6354  StoppedAtLocation = true;
6355  PlotStartPosition(5);
6357  // pale green
6360  TerminatedMessageSent = false;
6361  Utilities->CallLogPop(1022);
6362 }
6363 
6364 // ---------------------------------------------------------------------------
6365 
6366 void TTrain::RemainHere(int Caller)
6367 {
6368  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6369  if(RemainHereLogNotSent) // to prevent repeated logs
6370  {
6371  TrainController->LogEvent(Utilities->TimeStamp() + AnsiString(Caller) + ",RemainHere" + "," + HeadCode);
6372  RemainHereLogNotSent = false;
6373  }
6375  {
6376  Display->PerformanceLog(5, Utilities->Format96HHMMSS(TrainController->TTClockTime) + ": " + HeadCode + " terminated at " +
6379  TerminatedMessageSent = true;
6380  }
6381  TimetableFinished = true;
6382  Utilities->CallLogPop(1023);
6383 }
6384 
6385 // ---------------------------------------------------------------------------
6386 
6387 void TTrain::SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
6388 /*
6389  Enter with pointer at next expected action, and IncNum the number by which have to increase the pointer
6390  to reach the action that is valid for the train's current position. i.e. IncNum error messages to be sent
6391  except where an action is a departure, starting at the current value for the pointer
6392  If IncNum is -1, then send messages for all remaining actions, including Fer if present
6393  If IncNum is -2, then send messages for all remaining actions, except Fer if present
6394 */{
6395  if((Ptr->Command == "Snt") && Ptr->SignallerControl)
6396  {
6397  return; // if remove train that starts under signaller control no messages needed
6398 
6399  }
6400  TrainController->LogEvent("" + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6401  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendMissedActionLogs," + AnsiString(IncNum) + "," + HeadCode);
6402  if(IncNum > 0)
6403  {
6404  for(int x = 0; x < IncNum; x++)
6405  {
6406  if(x > 0)
6407  {
6408  Ptr++;
6409  }
6410  // arrival - no need to test for termination as this section only covers missed actions up to the
6411  // arrival point - may terminate later but that not missed
6412  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6413  {
6415  }
6416  // arrival & departure
6417  if(Ptr->FormatType == TimeTimeLoc)
6418  {
6420  }
6421  // departure
6422  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6423  {
6424  continue; // skip TimeLoc departures, message given for arrivals
6425  }
6426  // pass
6427  else if(Ptr->FormatType == PassTime)
6428  {
6430  }
6431  // split
6432  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6433  {
6435  }
6436  // jbo
6437  else if(Ptr->Command == "jbo")
6438  {
6440  }
6441  // Errors - have reached a station stop point (before a cdt) during Train->Update() so intervening actions can't
6442  // be starts, finishes or cdt
6443  else if((Ptr->Command == "Fns") || (Ptr->Command == "Frh") || (Ptr->Command == "Fer") || (Ptr->Command == "Fjo") || (Ptr->Command == "Snt") ||
6444  (Ptr->Command == "Sfs") || (Ptr->Command == "Snt-sh") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") || (Ptr->Command == "Sns-fsh") ||
6445  (Ptr->Command == "cdt") || (Ptr->Command == "Frh-sh") || (Ptr->Command == "Fns-sh") || (Ptr->Command == "F-nshs") ||
6446  (Ptr->FormatType == Repeat))
6447  {
6448  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6449  }
6450  }
6451  }
6452  else
6453  {
6454  bool IncludeFER = false;
6455  if(IncNum == -1)
6456  {
6457  IncludeFER = true;
6458  }
6459  while(true) // finish commands & repeats break out of loop
6460  {
6461  // Fer & excluded - send normal exit log to give minutes late or early - no, have already sent an unexpected exit message
6462  if(!IncludeFER && (Ptr->Command == "Fer"))
6463  {
6464  break;
6465  }
6466  // Fer & included
6467  else if(IncludeFER && (Ptr->Command == "Fer"))
6468  {
6470  break;
6471  }
6472  // Repeat
6473  else if(Ptr->FormatType == Repeat)
6474  {
6475  break;
6476  }
6477  // Fjo
6478  else if(Ptr->Command == "Fjo")
6479  {
6481  break;
6482  }
6483  // Frh
6484  else if(Ptr->Command == "Frh")
6485  {
6487  {
6489  TerminatedMessageSent = true;
6490  }
6491  break;
6492  }
6493  // Frh-sh
6494  else if(Ptr->Command == "Frh-sh")
6495  {
6497  {
6499  TerminatedMessageSent = true;
6500  }
6501  break;
6502  }
6503  // Fns, F-nshs, Fns-sh
6504  else if((Ptr->Command == "Fns") || (Ptr->Command == "F-nshs") || (Ptr->Command == "Fns-sh"))
6505  {
6507  break;
6508  }
6509  // end of breakout actions
6510  // arrival
6511  if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime > TDateTime(-1)))
6512  {
6513  if(IsTrainTerminating(1))
6514  {
6516  TerminatedMessageSent = true;
6517  }
6518  else
6519  {
6521  }
6522  }
6523  // arrival & departure
6524  else if(Ptr->FormatType == TimeTimeLoc)
6525  {
6527  }
6528  // departure
6529  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
6530  {
6531  Ptr++;
6532  continue; // skip TimeLoc departures, message given for arrivals
6533  }
6534  // pass
6535  else if(Ptr->FormatType == PassTime)
6536  {
6538  }
6539  // split
6540  else if((Ptr->Command == "fsp") || (Ptr->Command == "rsp"))
6541  {
6543  }
6544  // jbo
6545  else if(Ptr->Command == "jbo")
6546  {
6548  }
6549  // cdt
6550  else if(Ptr->Command == "cdt")
6551  {
6552 // TrainController->LogActionError(25, HeadCode, "", FailMissedChangeDirection, Ptr->LocationName); //commented out at v2.12.0 as cdts not counted as missed events
6553  }
6554  // Errors
6555  else if((Ptr->Command == "Snt-sh") || (Ptr->Command == "Sfs") || (Ptr->Command == "Sns") || (Ptr->Command == "Sns-sh") ||
6556  (Ptr->Command == "Sns-fsh") || ((Ptr->Command == "Snt") && !Ptr->SignallerControl))
6557  {
6558  throw Exception("Error - illegal command in SendMissedActionLogs for IncNum = " + AnsiString(IncNum) + ", and command = " + Ptr->Command);
6559  }
6560  Ptr++;
6561  }
6562  TimetableFinished = true;
6563  }
6564  Utilities->CallLogPop(1021);
6565 }
6566 
6567 // ---------------------------------------------------------------------------
6568 
6569 bool TTrain::TrainToJoinIsAdjacent(int Caller, TTrain* &TrainToJoin)
6570 // ensure same repeatnumber
6571 {
6572  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToJoinIsAdjacent" + "," + HeadCode);
6574 
6575  if(TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6576  {
6577  Utilities->CallLogPop(1024);
6578  return(false);
6579  }
6580  TrainToJoin = &(TrainController->TrainVectorAtIdent(33, TrainToJoinTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6581  if(TrainToJoin->StoppedAtLocation && (TrainToJoin->TrainMode == Timetable) && (TrainToJoin->ActionVectorEntryPtr->Command == "jbo"))
6582  {
6583  if((Track->TrackElementAt(610, LeadElement).Conn[LeadExitPos] == TrainToJoin->LeadElement) || (Track->TrackElementAt(611,
6584  LeadElement).Conn[LeadExitPos] == TrainToJoin->MidElement) || (Track->TrackElementAt(612, MidElement).Conn[MidEntryPos] == TrainToJoin->LeadElement)
6585  || (Track->TrackElementAt(613, MidElement).Conn[MidEntryPos] == TrainToJoin->MidElement))
6586  {
6587  Utilities->CallLogPop(1025);
6588  return(true);
6589  }
6590  }
6591  Utilities->CallLogPop(1026);
6592  return(false);
6593 }
6594 
6595 // ---------------------------------------------------------------------------
6596 
6597 bool TTrain::TrainToBeJoinedByIsAdjacent(int Caller, TTrain* &TrainToBeJoinedBy)
6598 // ensure same repeatnumber
6599 {
6600  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainToBeJoinedByIsAdjacent" + "," + HeadCode);
6601  TTrainDataEntry *TrainToBeJoinedByTDEntry = ActionVectorEntryPtr->LinkedTrainEntryPtr;
6602 
6603  if(TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).RunningEntry != Running)
6604  {
6605  Utilities->CallLogPop(1027);
6606  return(false);
6607  }
6608  TrainToBeJoinedBy = &(TrainController->TrainVectorAtIdent(15, TrainToBeJoinedByTDEntry->TrainOperatingDataVector.at(RepeatNumber).TrainID));
6609  if(TrainToBeJoinedBy->StoppedAtLocation && (TrainToBeJoinedBy->TrainMode == Timetable) && (TrainToBeJoinedBy->ActionVectorEntryPtr->Command == "Fjo"))
6610  {
6611  if((Track->TrackElementAt(614, LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(615,
6612  LeadElement).Conn[LeadExitPos] == TrainToBeJoinedBy->MidElement) || (Track->TrackElementAt(616,
6613  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->LeadElement) || (Track->TrackElementAt(617,
6614  MidElement).Conn[MidEntryPos] == TrainToBeJoinedBy->MidElement))
6615  {
6616  Utilities->CallLogPop(1028);
6617  return(true);
6618  }
6619  }
6620  Utilities->CallLogPop(1029);
6621  return(false);
6622 }
6623 
6624 // ---------------------------------------------------------------------------
6625 
6626 void TTrain::NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6627 { //same RepeatNumber (i.e. 0) used for the new shuttle
6628  TrainController->LogEvent("" + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6629  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",NewShuttleFromNonRepeatService" + "," + HeadCode);
6630  if(PowerAtRail < 1)
6631  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6632  {
6634  {
6635  TrainController->StopTTClockMessage(88, HeadCode + ": A train without power can't form a new service");
6636  }
6638  Utilities->CallLogPop(2143);
6639  return;
6640  }
6641  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6642 
6643  if(!NoLogFlag)
6644  {
6646  }
6647  UnplotTrain(4);
6650  StartSpeed = 0;
6655  HeadCode = NewHeadCode;
6656  IncrementalMinutes = TrainDataEntryPtr->ActionVector.back().RearStartOrRepeatMins;
6657  IncrementalDigits = TrainDataEntryPtr->ActionVector.back().FrontStartOrRepeatDigits;
6658  StoppedAtLocation = true;
6659  PlotStartPosition(6);
6661  // pale green
6664  TerminatedMessageSent = false;
6665  Utilities->CallLogPop(1078);
6666 }
6667 
6668 // ---------------------------------------------------------------------------
6669 
6670 void TTrain::RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips)
6671 // need to check whether all repeats finished or not
6672 {
6673  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6674  if(RemainHereLogNotSent) // to prevent repeated logs
6675  {
6676  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrRemainHere" + "," + HeadCode);
6677  RemainHereLogNotSent = false;
6678  }
6680  // finished all repeats
6681  {
6683  { //no need to suppress this LogAction because BecomeNewService won't be available in this case
6686  TerminatedMessageSent = true;
6687  // no need to clear message as no more actions
6688  }
6689  TimetableFinished = true;
6690  Utilities->CallLogPop(1080);
6691  return;
6692  }
6693  if(PowerAtRail < 1)
6694  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6695  {
6697  {
6698  TrainController->StopTTClockMessage(89, HeadCode + ": A train without power can't form a new service");
6699  }
6701  Utilities->CallLogPop(2144);
6702  return;
6703  }
6704  int TempRepeatNumber = RepeatNumber + 1;
6705  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6706  // until after LogAction or the wrong time will be used
6707  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(6, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6708 
6709  if(!NoLogFlag)
6710  {
6712  }
6713  RepeatNumber++;
6714  UnplotTrain(5);
6717  StartSpeed = 0;
6722  HeadCode = NewHeadCode;
6723  StoppedAtLocation = true;
6724  PlotStartPosition(7);
6726  // pale green
6729  TerminatedMessageSent = false;
6730  Utilities->CallLogPop(1079);
6731 }
6732 
6733 // ---------------------------------------------------------------------------
6734 
6735 void TTrain::RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag) //bool NoLogFlag added at v2.12.0 for new service TT skips
6736 {
6737  TrainController->LogEvent("" + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6738  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RepeatShuttleOrNewNonRepeatService" + "," + HeadCode);
6739  if(PowerAtRail < 1)
6740  // new at v2.4.0 (ActionVectorEntryPtr not incremented so can for new service when power restored)
6741  {
6743  {
6744  TrainController->StopTTClockMessage(90, HeadCode + ": A train without power can't form a new service");
6745  }
6747  Utilities->CallLogPop(2145);
6748  return;
6749  }
6751  // finished all repeats
6752  {
6753  AnsiString NewHeadCode = ActionVectorEntryPtr->NonRepeatingShuttleLinkHeadCode;
6754  if(!NoLogFlag)
6755  {
6757  }
6758  RepeatNumber = 0;
6759  UnplotTrain(6);
6762  StartSpeed = 0;
6764  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).TrainID = TrainID; // but note that RepeatNumber = 0
6765  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).RunningEntry = Running; // but note that RepeatNumber = 0
6767  HeadCode = NewHeadCode;
6768  StoppedAtLocation = true;
6769  PlotStartPosition(9);
6773  TerminatedMessageSent = false;
6774  Utilities->CallLogPop(1081);
6775  return;
6776  }
6777  int TempRepeatNumber = RepeatNumber + 1;
6778  // need the next repeat value in order to obtain a correct NewHeadCode, but don't increase it
6779  // until after LogAction or the wrong time will be used
6780  AnsiString NewHeadCode = TrainController->GetRepeatHeadCode(7, ActionVectorEntryPtr->OtherHeadCode, TempRepeatNumber, IncrementalDigits);
6781 
6782  if(!NoLogFlag)
6783  {
6785  }
6786  RepeatNumber++;
6787  UnplotTrain(7);
6790  StartSpeed = 0;
6795  HeadCode = NewHeadCode;
6796  StoppedAtLocation = true;
6797  PlotStartPosition(8);
6799  // pale green
6802  TerminatedMessageSent = false;
6803  Utilities->CallLogPop(1082);
6804 }
6805 
6806 // ---------------------------------------------------------------------------
6807 
6809 {
6810  // Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
6811  // entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
6812  // must be preceded by a TimeLoc departure
6813  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsTrainTerminating" + "," + HeadCode);
6814  for(unsigned int x = 1; x < TrainDataEntryPtr->ActionVector.size(); x++)
6815  {
6817  {
6818  if(((ActionVectorEntryPtr + x)->Command == "Fer") || ((ActionVectorEntryPtr + x)->FormatType == TimeLoc))
6819  {
6820  Utilities->CallLogPop(1083);
6821  return(false);
6822  }
6823  else if((ActionVectorEntryPtr + x)->SequenceType == Finish)
6824  {
6825  Utilities->CallLogPop(1084);
6826  return(true);
6827  }
6828  }
6829  }
6830  Utilities->CallLogPop(1085);
6831  return(false);
6832 }
6833 
6834 // ---------------------------------------------------------------------------
6835 
6836 bool TTrain::AbleToMove(int Caller)
6837 {
6838  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMove" + "," + HeadCode);
6839  bool Able = true;
6840 
6841  if(Crashed || Derailed || StoppedAtBuffers || StoppedAtSignal || StoppedWithoutPower) // StoppedWithoutPower added at v2.4.0
6842  {
6843  // StoppedForTrainInFront removed as tested below
6844  Utilities->CallLogPop(2146); // added v2.4.0
6845  return(false); // added v2.4.0
6846  }
6847  if(LeadElement > -1)
6848  {
6849  if(Track->TrackElementAt(801, LeadElement).TrackType == Buffers) //moved up here from 'else' below at v2.12.0
6850  {
6851  StoppedForTrainInFront = false;
6852  // don't set StoppedAtBuffers as (presumably) StoppedAtLocation & leave it at that
6853  Utilities->CallLogPop(2456);
6854  return(false);
6855  }
6856  int FrontPos = Track->TrackElementAt(678, LeadElement).Conn[LeadExitPos];
6857  int FrontEntryPos = Track->TrackElementAt(679, LeadElement).ConnLinkPos[LeadExitPos];
6858  if((FrontPos > -1) && (TrainMode == Signaller) && StoppedForTrainInFront) //check if train in front still there
6859  {
6860  TTrackElement TrackElement = Track->TrackElementAt(680, FrontPos);
6861  if((TrackElement.TrackType != Bridge) && (TrackElement.TrainIDOnElement == -1))
6862  {
6863  Able = true;
6864  StoppedForTrainInFront = false;
6865  }
6866  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos < 2) && (TrackElement.TrainIDOnBridgeTrackPos01 == -1))
6867  {
6868  Able = true;
6869  StoppedForTrainInFront = false;
6870  }
6871  else if((TrackElement.TrackType == Bridge) && (FrontEntryPos > 1) && (TrackElement.TrainIDOnBridgeTrackPos23 == -1))
6872  {
6873  Able = true;
6874  StoppedForTrainInFront = false;
6875  }
6876  else
6877  {
6878  Able = false; //added at v2.12.0 as train still in front so don't want signaller popup options to move available
6879  }
6880  }
6881  }
6882  else // leaving at a continuation so keep going
6883  {
6884  Able = true;
6885  StoppedForTrainInFront = false;
6886  }
6887  Utilities->CallLogPop(1454);
6888  return(Able);
6889 }
6890 
6891 // ---------------------------------------------------------------------------
6892 
6894 {
6895  // first check if a train immediately in front (may have moved there since this train stopped so StoppedForTrainInFront
6896  // won't be set; if there is a train then set StoppedForTrainInFront
6897  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AbleToMoveButForSignal" + "," + HeadCode);
6898  // addition below for v1.3.2 after Carwyn Thomas fault reported 24/05/15 - need to check if exiting at continuation (LeadElement == -1) as if so fails at VecPos = .....
6899  if(LeadElement == -1) // exiting at continuation
6900  {
6901  Utilities->CallLogPop(2045);
6902  return(false);
6903  }
6904  // end of addition
6905  int VecPos = Track->TrackElementAt(654, LeadElement).Conn[LeadExitPos];
6906  int NextEntryPos = Track->TrackElementAt(655, LeadElement).ConnLinkPos[LeadExitPos];
6907 
6908  if(Track->OtherTrainOnTrack(5, VecPos, NextEntryPos, TrainID))
6909  {
6910  StoppedForTrainInFront = true;
6911  Utilities->CallLogPop(1455);
6912  return(false);
6913  }
6914  else
6915  {
6916  Utilities->CallLogPop(1456);
6918  // StoppedWithoutPower added v2.4.0
6919  }
6920 }
6921 
6922 // ---------------------------------------------------------------------------
6923 
6925 {
6926  // unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd
6927  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SignallerChangeTrainDirection" + "," + HeadCode);
6928  TColor TempColour = BackgroundColour;
6929 
6930  UnplotTrain(8);
6933  StartSpeed = 0;
6934  PlotStartPosition(2);
6935  PlotTrainWithNewBackgroundColour(26, TempColour, Display);
6936 
6937  //now erase a stub route if there is one, added at v2.5.1
6938  //first element of route is now immediately behind the train (i.e. next to MidElement)
6939  if(MidEntryPos >= 0)
6940  {
6941  TTrackElement MidTrackElement = Track->TrackElementAt(1000, MidElement);
6942  int FirstRouteElementVecPos = MidTrackElement.Conn[MidEntryPos];
6943  int FirstRouteLinkPos = MidTrackElement.ConnLinkPos[MidEntryPos];
6944  int RouteNumber = -1;
6945  TAllRoutes::TRouteType RouteType = AllRoutes->GetRouteTypeAndNumber(35, FirstRouteElementVecPos, FirstRouteLinkPos, RouteNumber);
6946  if(RouteType == TAllRoutes::NotAutoSigsRoute)
6947  {
6948  TOneRoute &OR = AllRoutes->GetModifiableRouteAt(29, RouteNumber);
6949  TTrackElement TE = Track->TrackElementAt(1001, FirstRouteElementVecPos);
6950  if((TE.TrackType != SignalPost) && (TE.TrackType != Continuation)) //all autosigs routes have signalpost or continuation at 0 so they are automatically excluded
6951  {
6952  bool FirstPass = true; //added at v2.8.0
6953  while(OR.PrefDirSize() > 0) //remove the route up to but not including the next facing signal, in case a route extends to another signal
6954  {
6955  TPrefDirElement PDE = OR.GetFixedPrefDirElementAt(248, 0); //these will change at each element removal because OR is a reference to the real route
6956  int TVPos2 = PDE.GetTrackVectorPosition();
6957  if(FirstPass && (TVPos2 != FirstRouteElementVecPos)) //route is not directed away from cdt train, could be a call-on for another train (added at v2.8.0)
6958  {
6959  break;
6960  }
6961  TTrackElement TE2 = Track->TrackElementAt(1002, TVPos2);
6963  {
6964  AllRoutes->RemoveRouteElement(23, TE2.HLoc, TE2.VLoc, PDE.GetELink());
6965  }
6966  else
6967  {
6968  break;
6969  }
6970  FirstPass = false;
6971  }
6972  AllRoutes->RebuildRailwayFlag = true;
6973  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot without stub route
6974  }
6975  }
6976  }
6977  Utilities->CallLogPop(1102);
6978 }
6979 
6980 // ---------------------------------------------------------------------------
6981 
6983 {
6984  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
6985  ",FloatingLabelNextString" + "," + HeadCode);
6986  AnsiString RetStr = "", LocationName = "";
6987 
6988  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
6989  {
6990  throw Exception("Error - start entry in FloatingLabelNextString");
6991  }
6992  if(Ptr->FormatType == TimeTimeLoc)
6993  {
6994  if(TrainMode == Timetable)
6995  {
6996  if(!TrainAtLocation(0, LocationName) || (LocationName != Ptr->LocationName))
6997  // not arrived yet in tt mode
6998  {
6999  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(2, Ptr->ArrivalTime));
7000  }
7001  else
7002  {
7003  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(3, Ptr->DepartureTime));
7004  }
7005  }
7006  else // TrainMode == Signaller
7007  {
7008  if(!DepartureTimeSet) // not arrived yet
7009  {
7010  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(45, Ptr->ArrivalTime));
7011  }
7012  else
7013  {
7014  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(36, Ptr->DepartureTime));
7015  }
7016  }
7017  }
7018  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7019  {
7020  RetStr = "Arrive " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(4, Ptr->ArrivalTime));
7021  }
7022  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7023  {
7024  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(5, Ptr->DepartureTime));
7025  }
7026  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewservice)
7027  {
7028  RetStr = "Depart " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(46, Ptr->EventTime));
7029  }
7030  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7031  {
7032  RetStr = "Pass " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(31, Ptr->EventTime));
7033  }
7034  else if(Ptr->Command == "Fns")
7035  {
7036  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(8, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " +
7037  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(6, Ptr->EventTime));
7038  RetStr = GetNewServiceDepartureInfo(0, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7039  }
7040  else if(Ptr->Command == "F-nshs")
7041  {
7042  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName + " at " +
7044  RetStr = GetNewServiceDepartureInfo(1, Ptr, 0, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7045  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7046  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7047  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7048  }
7049  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7050  {
7051  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(9, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7052  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(7, Ptr->EventTime));
7053  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7054  RetStr = GetNewServiceDepartureInfo(2, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7055  }
7056  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7057  {
7058  RetStr = "Forms new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7059  +" at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(8, Ptr->EventTime));
7060  RetStr = GetNewServiceDepartureInfo(3, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7061  }
7062  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not last repeat number
7063  {
7064  RetStr = "Forms new service " + TrainController->GetRepeatHeadCode(10, Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " +
7065  Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(9, Ptr->EventTime));
7066  // use RepeatNumber+1 as it's the repeat number of the NEXT shuttle service that is relevant
7067  RetStr = GetNewServiceDepartureInfo(4, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, RetStr); //if there is a next service this adds the new service departure time to RetStr
7068  }
7069  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7070  {
7071  RetStr ="None, train terminated at " + Ptr->LocationName;
7072  }
7073  else if(Ptr->Command == "Frh")
7074  {
7075  RetStr = "None, train terminated at " + Ptr->LocationName;
7076  }
7077  else if(Ptr->Command == "Fer")
7078  {
7079  AnsiString AllowedExits = "";
7080  RetStr = "Exit railway" + TrainController->GetExitLocationAndAt(1, Ptr->ExitList, AllowedExits) + " at " + Utilities->Format96HHMM(GetTrainTime(10, Ptr->EventTime)) + AllowedExits;
7081  }
7082  else if(Ptr->Command == "Fjo")
7083  {
7084  RetStr = "Join " + TrainController->GetRepeatHeadCode(11, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName + " at " +
7086  }
7087  else if(Ptr->Command == "jbo")
7088  {
7089  RetStr = "Joined by " + TrainController->GetRepeatHeadCode(12, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7090  " at " + Utilities->Format96HHMM(GetTrainTime(12, Ptr->EventTime));
7091  }
7092  else if(Ptr->Command == "fsp")
7093  {
7094  RetStr = "Front split to " + TrainController->GetRepeatHeadCode(13, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7095  " at " + Utilities->Format96HHMM(GetTrainTime(13, Ptr->EventTime));
7096  }
7097  else if(Ptr->Command == "rsp")
7098  {
7099  RetStr = "Rear split to " + TrainController->GetRepeatHeadCode(14, Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName +
7100  " at " + Utilities->Format96HHMM(GetTrainTime(14, Ptr->EventTime));
7101  }
7102  else if(Ptr->Command == "cdt")
7103  {
7104  RetStr = "Change direction at " + Ptr->LocationName + " at " + Utilities->Format96HHMM(GetTrainTime(15, Ptr->EventTime));
7105  }
7106  Utilities->CallLogPop(1124);
7107  return(RetStr);
7108 }
7109 
7110 // ---------------------------------------------------------------------------
7111 
7112 AnsiString TTrain::GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
7113 {
7114  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) + ","
7115  + AnsiString(RptNum) + ",GetNewServiceDepartureInfo," + HeadCode);
7116  AnsiString DepTime = "", EventTime = "";
7117  bool CDTFlag = false; //reports if train changes direction before departs
7118  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
7119  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
7120  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
7121  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
7122  {
7123  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
7124  {
7125  TowardsLocation = AVI->LocationName;
7126  }
7127  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
7128  {
7129  TTrackElement TE = Track->TrackElementAt(7777, (AVI->ExitList.front()));
7130  if(TE.ActiveTrackElementName != "")
7131  {
7132  TowardsLocation = TE.ActiveTrackElementName;
7133  }
7134  else
7135  {
7136  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
7137  }
7138  }
7139  }
7140  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
7141  {
7142  if(AVI->Command == "cdt")
7143  {
7144  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
7145  continue;
7146  }
7147  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
7148  {
7149  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(19, AVI->EventTime, RptNum, IncrementalMinutes));
7150  RetStr += "\nNew service splits at " + EventTime;
7151  Utilities->CallLogPop(2234);
7152  return(RetStr);
7153  }
7154  if(AVI->Command == "jbo")
7155  {
7156  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(20, AVI->EventTime, RptNum, IncrementalMinutes));
7157  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
7158  Utilities->CallLogPop(2235);
7159  return(RetStr);
7160  }
7161  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
7162  {
7163  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(17, AVI->DepartureTime, RptNum, IncrementalMinutes));
7164  if(CDTFlag)
7165  {
7166  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7167  {
7168  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
7169  }
7170  else
7171  {
7172  RetStr += "\nNew service changes direction then departs at " + DepTime;
7173  }
7174  }
7175  else
7176  {
7177  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
7178  {
7179  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
7180  }
7181  else
7182  {
7183  RetStr += "\nNew service departs at " + DepTime;
7184  }
7185  }
7186  Utilities->CallLogPop(2236);
7187  return(RetStr);
7188  }
7189  }
7190  Utilities->CallLogPop(2208);
7191  return(RetStr); //if reach here then RetStr doesn't change
7192 }
7193 
7194 // ---------------------------------------------------------------------------
7195 
7197 // Enter with Ptr pointing to first action to be listed (i.e. next action)
7198 // If there are actions to be skipped but a departure is awaited (SkippedDeparture = true) then after the departure Ptr moves forward by SkipPtrValue
7199 {
7200  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TrainDataEntryPtr->ActionVector.front()) +
7201  ",FloatingTimetableString" + "," + HeadCode);
7202  AnsiString RetStr = "", PartStr = "";
7203  int Count = 0;
7204  bool SkipDep = false, SkipDepActedOn = false; //SkipDepActedOn ensures only one SkipDep acted on
7205  AnsiString LocName = Ptr->LocationName;
7206 
7207  if((Ptr->Command != "") && (Ptr->Command[1] == 'S') && (TrainMode == Timetable))
7208  // can start in signaller control so exclude this
7209  {
7210  throw Exception("Error - start entry in FloatingTimetableString");
7211  }
7212  TActionVectorEntry *EntryPtr = Ptr; //used in TimeTimeLoc check later
7213  bool FirstPass = true;
7214  Ptr--; // because incremented at start of loop
7215 
7216  // different first TimeTimeLoc display if in signaller control
7217  do
7218  {
7219  Ptr++;
7220  if((Ptr->FormatType == Repeat) || TimetableFinished)
7221  {
7222  break;
7223  }
7224  if((Ptr->FormatType == TimeTimeLoc) && FirstPass)
7225  {
7226  AnsiString TrainLoc = "";
7227  if(TrainMode == Timetable)
7228  {
7229  if(TrainAtLocation(1, TrainLoc) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7230  {
7231  PartStr = Utilities->Format96HHMM(GetTrainTime(33, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7232  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7233  {
7234  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7235  }
7236  }
7237  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7238  {
7239  PartStr = Utilities->Format96HHMM(GetTrainTime(34, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7240  }
7241  else
7242  {
7243  PartStr = Utilities->Format96HHMM(GetTrainTime(16, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7244  Utilities->Format96HHMM(GetTrainTime(17, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7245  Count++; // because there are 2 entries
7246  }
7247  }
7248  else // TrainMode == Signaller
7249  {
7250  if(DepartureTimeSet)
7251  {
7252  PartStr = Utilities->Format96HHMM(GetTrainTime(37, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7253  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7254  {
7255  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7256  }
7257  }
7258  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7259  {
7260  PartStr = Utilities->Format96HHMM(GetTrainTime(38, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7261  }
7262  else
7263  {
7264  PartStr = Utilities->Format96HHMM(GetTrainTime(39, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7265  Utilities->Format96HHMM(GetTrainTime(40, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7266  Count++; // because there are 2 entries
7267  }
7268  }
7269  }
7270  else if((Ptr->FormatType == TimeTimeLoc) && !FirstPass)
7271  {
7272  AnsiString TrainLoc = "";
7273  if((TrainAtLocation(2, TrainLoc)) && (TrainLoc == Ptr->LocationName) && (Ptr == EntryPtr)) //added '&& (Ptr == EntryPtr)' at v2.6.0 when allow multiple same location entries
7274  {
7275  PartStr = Utilities->Format96HHMM(GetTrainTime(41, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7276  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7277  {
7278  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7279  }
7280  }
7281  else if(Ptr->ArrivalTime == Ptr->DepartureTime)
7282  {
7283  PartStr = Utilities->Format96HHMM(GetTrainTime(42, Ptr->ArrivalTime)) + ": Arrive & depart from " + Ptr->LocationName;
7284  }
7285  else
7286  {
7287  PartStr = Utilities->Format96HHMM(GetTrainTime(43, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName + '\n' +
7288  Utilities->Format96HHMM(GetTrainTime(44, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7289  Count++; // because there are 2 entries
7290  }
7291  }
7292  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
7293  {
7294  PartStr = Utilities->Format96HHMM(GetTrainTime(18, Ptr->ArrivalTime)) + ": Arrive at " + Ptr->LocationName;
7295  }
7296  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
7297  {
7298  PartStr = Utilities->Format96HHMM(GetTrainTime(19, Ptr->DepartureTime)) + ": Depart from " + Ptr->LocationName;
7299  if((LocName == Ptr->LocationName) && (LocName != "") && SkippedDeparture && !SkipDepActedOn)
7300  {
7301  SkipDep = true; //0 for incremental minutes because don't reduce the departure time when later actions have been skipped
7302  }
7303  }
7304  else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture) //added at v2.12.0 for becoming new service early (see BecomeNewService)
7305  { //note that TreatPassAsTimeLocDeparture can't be set if have SkippedDeparture
7306  PartStr = Utilities->Format96HHMM(GetTrainTime(47, Ptr->EventTime)) + ": Depart from " + Ptr->LocationName;
7307  }
7308  else if(Ptr->FormatType == PassTime) //must come after 'else if((Ptr->FormatType == PassTime) && TreatPassAsTimeLocDeparture)'
7309  {
7310  PartStr = Utilities->Format96HHMM(GetTrainTime(30, Ptr->EventTime)) + ": Pass " + Ptr->LocationName;
7311  }
7312  else if(Ptr->Command == "Fns")
7313  {
7314  PartStr = Utilities->Format96HHMM(GetTrainTime(20, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(15,
7315  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7316  PartStr = GetNewServiceDepartureInfo(5, Ptr, RepeatNumber, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to PartStr
7317  }
7318  else if(Ptr->Command == "F-nshs")
7319  {
7320  PartStr = Utilities->Format96HHMM(GetTrainTime(35, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode + " at " +
7321  Ptr->LocationName;
7322  PartStr = GetNewServiceDepartureInfo(6, Ptr, 0, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7323  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
7324  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
7325  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
7326  }
7327  else if((Ptr->Command == "Fns-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7328  {
7329  PartStr = Utilities->Format96HHMM(GetTrainTime(21, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(16,
7330  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7331  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7332  PartStr = GetNewServiceDepartureInfo(7, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7333  }
7334  else if((Ptr->Command == "Fns-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7335  {
7336  PartStr = Utilities->Format96HHMM(GetTrainTime(22, Ptr->EventTime)) + ": Form new service " + Ptr->NonRepeatingShuttleLinkHeadCode,
7337  +" at " + Ptr->LocationName;
7338  PartStr = GetNewServiceDepartureInfo(8, Ptr, 0, Ptr->NonRepeatingShuttleLinkEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7339  }
7340  else if((Ptr->Command == "Frh-sh") && (RepeatNumber < (TrainDataEntryPtr->NumberOfTrains - 1))) // not the last repeat number
7341  {
7342  PartStr = Utilities->Format96HHMM(GetTrainTime(23, Ptr->EventTime)) + ": Form new service " + TrainController->GetRepeatHeadCode(17,
7343  Ptr->OtherHeadCode, RepeatNumber + 1, IncrementalDigits) + " at " + Ptr->LocationName;
7344  // use RepeatNumber+1 because it's the repeat number of the NEXT shuttle service that is relevant
7345  PartStr = GetNewServiceDepartureInfo(9, Ptr, RepeatNumber + 1, Ptr->LinkedTrainEntryPtr, PartStr); //if there is a next service this adds the new service departure time to RetStr
7346  }
7347  else if((Ptr->Command == "Frh-sh") && (RepeatNumber >= (TrainDataEntryPtr->NumberOfTrains - 1))) // last repeat number
7348  {
7349  PartStr = "Terminate at " + Ptr->LocationName;
7350  }
7351  else if(Ptr->Command == "Frh")
7352  {
7353  PartStr = "Terminate at " + Ptr->LocationName;
7354  }
7355  else if(Ptr->Command == "Fer")
7356  {
7357  AnsiString AllowedExits = "";
7358  PartStr = Utilities->Format96HHMM(GetTrainTime(24, Ptr->EventTime)) + ": Exit railway" + TrainController->GetExitLocationAndAt(2, Ptr->ExitList, AllowedExits) + AllowedExits;
7359  }
7360  else if(Ptr->Command == "Fjo")
7361  {
7362  PartStr = Utilities->Format96HHMM(GetTrainTime(25, Ptr->EventTime)) + ": Join " + TrainController->GetRepeatHeadCode(18, Ptr->OtherHeadCode,
7363  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7364  }
7365  else if(Ptr->Command == "jbo")
7366  {
7367  PartStr = Utilities->Format96HHMM(GetTrainTime(26, Ptr->EventTime)) + ": Joined by " + TrainController->GetRepeatHeadCode(19, Ptr->OtherHeadCode,
7368  RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7369  }
7370  else if(Ptr->Command == "fsp")
7371  {
7372  PartStr = Utilities->Format96HHMM(GetTrainTime(27, Ptr->EventTime)) + ": Front split to " + TrainController->GetRepeatHeadCode(20,
7373  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7374  }
7375  else if(Ptr->Command == "rsp")
7376  {
7377  PartStr = Utilities->Format96HHMM(GetTrainTime(28, Ptr->EventTime)) + ": Rear split to " + TrainController->GetRepeatHeadCode(21,
7378  Ptr->OtherHeadCode, RepeatNumber, IncrementalDigits) + " at " + Ptr->LocationName;
7379  }
7380  else if(Ptr->Command == "cdt")
7381  {
7382  PartStr = Utilities->Format96HHMM(GetTrainTime(29, Ptr->EventTime)) + ": Change direction at " + Ptr->LocationName;
7383  }
7384  if(RetStr != "")
7385  {
7386  RetStr = RetStr + '\n' + PartStr;
7387  }
7388  else
7389  {
7390  RetStr = PartStr;
7391  }
7392  FirstPass = false;
7393  Count++;
7394 
7395  if(SkipDep)
7396  {
7397  Ptr = &(TrainDataEntryPtr->ActionVector.at(0)) + SkipPtrValue;
7398  Ptr--; //it is incremented at the start of the next loop
7399  SkipDep = false;
7400  SkipDepActedOn = true;
7401  }
7402  }
7403  while(!TimetableFinished && (Count < 32) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
7404  // limit of 32 allows a max of 34 entries (33 + 1 for the new service departure time) (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and
7405  // train status gives a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
7406  // forward as anyone should wish to see without looking at the full timetable
7407  if(TimetableFinished)
7408  {
7409  if(TrainMode == Timetable)
7410  {
7411  RetStr = "Timetable finished";
7412  }
7413  else
7414  {
7415  RetStr = "No timetable";
7416  }
7417  }
7418  Utilities->CallLogPop(1125);
7419  return(RetStr);
7420 }
7421 
7422 // ---------------------------------------------------------------------------
7423 
7424 void TTrain::SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
7425 {
7426  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveOneSessionTrain" + "," + HeadCode);
7427  Utilities->SaveFileString(OutFile, HeadCode);
7430  Utilities->SaveFileInt(OutFile, StartSpeed);
7433  Utilities->SaveFileInt(OutFile, RepeatNumber);
7436  Utilities->SaveFileInt(OutFile, Mass);
7439  Utilities->SaveFileDouble(OutFile, EntrySpeed);
7446  Utilities->SaveFileDouble(OutFile, BrakeRate);
7450  Utilities->SaveFileDouble(OutFile, double(EntryTime));
7451  Utilities->SaveFileDouble(OutFile, double(ExitTimeHalf));
7452  Utilities->SaveFileDouble(OutFile, double(ExitTimeFull));
7453  Utilities->SaveFileDouble(OutFile, double(ReleaseTime));
7454  Utilities->SaveFileDouble(OutFile, double(TRSTime));
7455  Utilities->SaveFileDouble(OutFile, double(LastActionTime));
7459  Utilities->SaveFileInt(OutFile, (short)TrainMode);
7464  Utilities->SaveFileBool(OutFile, Derailed);
7466  Utilities->SaveFileBool(OutFile, Crashed);
7473  Utilities->SaveFileBool(OutFile, NotInService);
7474  Utilities->SaveFileBool(OutFile, Plotted);
7475  Utilities->SaveFileBool(OutFile, TrainGone);
7476  Utilities->SaveFileBool(OutFile, SPADFlag);
7478  Utilities->SaveFileInt(OutFile, HOffset[0]);
7479  Utilities->SaveFileInt(OutFile, HOffset[1]);
7480  Utilities->SaveFileInt(OutFile, HOffset[2]);
7481  Utilities->SaveFileInt(OutFile, HOffset[3]);
7482  Utilities->SaveFileInt(OutFile, VOffset[0]);
7483  Utilities->SaveFileInt(OutFile, VOffset[1]);
7484  Utilities->SaveFileInt(OutFile, VOffset[2]);
7485  Utilities->SaveFileInt(OutFile, VOffset[3]);
7486  Utilities->SaveFileInt(OutFile, PlotElement[0]);
7487  Utilities->SaveFileInt(OutFile, PlotElement[1]);
7488  Utilities->SaveFileInt(OutFile, PlotElement[2]);
7489  Utilities->SaveFileInt(OutFile, PlotElement[3]);
7490  Utilities->SaveFileInt(OutFile, PlotEntryPos[0]);
7491  Utilities->SaveFileInt(OutFile, PlotEntryPos[1]);
7492  Utilities->SaveFileInt(OutFile, PlotEntryPos[2]);
7493  Utilities->SaveFileInt(OutFile, PlotEntryPos[3]);
7495  Utilities->SaveFileInt(OutFile, (short)Straddle);
7496  Utilities->SaveFileInt(OutFile, NextTrainID);
7497  Utilities->SaveFileInt(OutFile, TrainID);
7498  Utilities->SaveFileInt(OutFile, LeadElement);
7499  Utilities->SaveFileInt(OutFile, LeadEntryPos);
7500  Utilities->SaveFileInt(OutFile, LeadExitPos);
7501  Utilities->SaveFileInt(OutFile, MidElement);
7502  Utilities->SaveFileInt(OutFile, MidEntryPos);
7503  Utilities->SaveFileInt(OutFile, MidExitPos);
7504  Utilities->SaveFileInt(OutFile, LagElement);
7505  Utilities->SaveFileInt(OutFile, LagEntryPos);
7506  Utilities->SaveFileInt(OutFile, LagExitPos);
7507  int ColourNumber;
7508 
7510  {
7511  ColourNumber = 0;
7512  }
7514  {
7515  ColourNumber = 1;
7516  }
7518  {
7519  ColourNumber = 2;
7520  }
7522  {
7523  ColourNumber = 3;
7524  }
7526  {
7527  ColourNumber = 4;
7528  }
7530  {
7531  ColourNumber = 5;
7532  }
7534  {
7535  ColourNumber = 6;
7536  }
7538  {
7539  ColourNumber = 7;
7540  }
7542  {
7543  ColourNumber = 8;
7544  }
7546  {
7547  ColourNumber = 9;
7548  }
7550  {
7551  ColourNumber = 10;
7552  }
7554  {
7555  ColourNumber = 11;
7556  }
7558  {
7559  ColourNumber = 12;
7560  }
7561  else if(BackgroundColour == clTRSBackground)
7562  {
7563  ColourNumber = 13;
7564  }
7566  {
7567  ColourNumber = 14; // added at v2.4.0
7568  }
7569  Utilities->SaveFileInt(OutFile, ColourNumber);
7570 
7571  // additional data
7572  bool ForwardHeadCode;
7573 
7574  if(HeadCodePosition[3] == HeadCodeGrPtr[3])
7575  {
7576  ForwardHeadCode = true;
7577  }
7578  // can't use 'if(HeadCodePosition[0] == HeadCodeGrPtr[0])' as HeadCodePosition[0] is set to FrontCodePtr
7579  else
7580  {
7581  ForwardHeadCode = false;
7582  }
7583  Utilities->SaveFileBool(OutFile, ForwardHeadCode);
7584 
7585  int TrainDataEntryValue = TrainDataEntryPtr - &(TrainController->TrainDataVector.at(0));
7586 
7587  Utilities->SaveFileInt(OutFile, TrainDataEntryValue);
7588  int ActionVectorEntryValue = ActionVectorEntryPtr - &(TrainDataEntryPtr->ActionVector.at(0));
7589 
7590  Utilities->SaveFileInt(OutFile, ActionVectorEntryValue);
7591  // now the marker comes next which was ****** originally but used for RestoreTimetableLocation as well some time ago (came before the asterisks)
7592  // but at v2.4.0 need to include StoppedWithoutPower, while keeping length of marker at 6, because that is tested in earlier versions
7593  // so use the last asterisk position for this - 0 for false & 1 for true
7594  // note that failed train data is handled in InterfaceUnit.cpp & stored after the performance file
7595  AnsiString Marker;
7596 
7598  {
7599  Marker = "*****1";
7600  }
7601  else
7602  {
7603  Marker = "*****0";
7604  }
7605  if(RestoreTimetableLocation == "")
7606  {
7607  Utilities->SaveFileString(OutFile, Marker);
7608  }
7609  else
7610  {
7611  AnsiString CombinedString = RestoreTimetableLocation + Marker;
7612  Utilities->SaveFileString(OutFile, CombinedString);
7613  // RestoreTimetableLocation + marker
7614  }
7615  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - it should have been saved earlier
7616  Utilities->CallLogPop(1457);
7617 }
7618 
7619 // ---------------------------------------------------------------------------
7620 
7621 void TTrain::LoadOneSessionTrain(int Caller, std::ifstream &InFile)
7622 {
7623  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadOneSessionTrain"); // don't have headcode yet
7624  HeadCode = Utilities->LoadFileString(InFile);
7627  StartSpeed = Utilities->LoadFileInt(InFile);
7629  if(SignallerMaxSpeed < 10)
7630  {
7631  SignallerMaxSpeed = 10; // added at v0.6 to avoid low max speeds
7632  }
7634  RepeatNumber = Utilities->LoadFileInt(InFile);
7637  Mass = Utilities->LoadFileInt(InFile);
7640  {
7642  }
7643  // above added at v2.1.0 for legacy session files where value may not have been limited
7645  EntrySpeed = Utilities->LoadFileDouble(InFile);
7649  if(TimetableMaxRunningSpeed < 10)
7650  {
7651  TimetableMaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7652  }
7654  if(MaxRunningSpeed < 10)
7655  {
7656  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
7657  }
7660  BrakeRate = Utilities->LoadFileDouble(InFile);
7664  EntryTime = TDateTime(Utilities->LoadFileDouble(InFile));
7665  ExitTimeHalf = TDateTime(Utilities->LoadFileDouble(InFile));
7666  ExitTimeFull = TDateTime(Utilities->LoadFileDouble(InFile));
7667  ReleaseTime = TDateTime(Utilities->LoadFileDouble(InFile));
7668  TRSTime = TDateTime(Utilities->LoadFileDouble(InFile));
7669  LastActionTime = TDateTime(Utilities->LoadFileDouble(InFile));
7678  Derailed = Utilities->LoadFileBool(InFile);
7680  Crashed = Utilities->LoadFileBool(InFile);
7687  NotInService = Utilities->LoadFileBool(InFile);
7688  Plotted = Utilities->LoadFileBool(InFile);
7689  TrainGone = Utilities->LoadFileBool(InFile);
7690  SPADFlag = Utilities->LoadFileBool(InFile);
7692  HOffset[0] = Utilities->LoadFileInt(InFile);
7693  HOffset[1] = Utilities->LoadFileInt(InFile);
7694  HOffset[2] = Utilities->LoadFileInt(InFile);
7695  HOffset[3] = Utilities->LoadFileInt(InFile);
7696  VOffset[0] = Utilities->LoadFileInt(InFile);
7697  VOffset[1] = Utilities->LoadFileInt(InFile);
7698  VOffset[2] = Utilities->LoadFileInt(InFile);
7699  VOffset[3] = Utilities->LoadFileInt(InFile);
7700  PlotElement[0] = Utilities->LoadFileInt(InFile);
7701  PlotElement[1] = Utilities->LoadFileInt(InFile);
7702  PlotElement[2] = Utilities->LoadFileInt(InFile);
7703  PlotElement[3] = Utilities->LoadFileInt(InFile);
7704  PlotEntryPos[0] = Utilities->LoadFileInt(InFile);
7705  PlotEntryPos[1] = Utilities->LoadFileInt(InFile);
7706  PlotEntryPos[2] = Utilities->LoadFileInt(InFile);
7707  PlotEntryPos[3] = Utilities->LoadFileInt(InFile);
7709  Straddle = (TStraddle)(Utilities->LoadFileInt(InFile));
7710  NextTrainID = Utilities->LoadFileInt(InFile);
7711  // will be same for all but best to save all anyway
7712  TrainID = Utilities->LoadFileInt(InFile);
7713  LeadElement = Utilities->LoadFileInt(InFile);
7714  LeadEntryPos = Utilities->LoadFileInt(InFile);
7715  LeadExitPos = Utilities->LoadFileInt(InFile);
7716  MidElement = Utilities->LoadFileInt(InFile);
7717  MidEntryPos = Utilities->LoadFileInt(InFile);
7718  MidExitPos = Utilities->LoadFileInt(InFile);
7719  LagElement = Utilities->LoadFileInt(InFile);
7720  LagEntryPos = Utilities->LoadFileInt(InFile);
7721  LagExitPos = Utilities->LoadFileInt(InFile);
7722  int ColourNumber = TColor(Utilities->LoadFileInt(InFile));
7723 
7724  if(ColourNumber == 0)
7725  {
7727  }
7728  else if(ColourNumber == 1)
7729  {
7731  }
7732  else if(ColourNumber == 2)
7733  {
7735  }
7736  else if(ColourNumber == 3)
7737  {
7739  }
7740  else if(ColourNumber == 4)
7741  {
7743  }
7744  else if(ColourNumber == 5)
7745  {
7747  }
7748  else if(ColourNumber == 6)
7749  {
7751  }
7752  else if(ColourNumber == 7)
7753  {
7755  }
7756  else if(ColourNumber == 8)
7757  {
7759  }
7760  else if(ColourNumber == 9)
7761  {
7763  }
7764  else if(ColourNumber == 10)
7765  {
7767  }
7768  else if(ColourNumber == 11)
7769  {
7771  }
7772  else if(ColourNumber == 12)
7773  {
7775  }
7776  else if(ColourNumber == 13)
7777  {
7779  }
7780  else if(ColourNumber == 14)
7781  {
7782  BackgroundColour = clTrainFailedBackground; // added at v2.4.0
7783 
7784  }
7785  // additional data
7787  // sets the BackgroundColour to the loaded value
7788  bool ForwardHeadCode = Utilities->LoadFileBool(InFile);
7789 
7790  if(ForwardHeadCode)
7791  {
7792  for(int x = 0; x < 4; x++)
7793  {
7795  }
7796  }
7797  else
7798  {
7799  for(int x = 0; x < 4; x++)
7800  {
7801  HeadCodePosition[x] = HeadCodeGrPtr[3 - x];
7802  }
7803  }
7804  // if crashed & in timetable mode then change FrontCodePtr to black, if in signaller mode then change to blue whether crashed or not
7805  if(TrainMode == Timetable)
7806  {
7807  if(Crashed)
7808  {
7810  }
7811  else
7812  {
7814  }
7815  }
7816  else
7817  {
7819  }
7821  // pick up background bitmaps, none if MidLag as no train plotted - entering at continuation
7822  if(Straddle == LeadMid)
7823  {
7824  if(LeadElement > -1)
7825  {
7827  }
7828  if(LeadElement > -1)
7829  {
7831  }
7832  if(MidElement > -1)
7833  {
7835  }
7836  if(MidElement > -1)
7837  {
7839  }
7840  }
7841  else if(Straddle == LeadMidLag)
7842  {
7843  if(LeadElement > -1)
7844  {
7846  }
7847  if(MidElement > -1)
7848  {
7850  }
7851  if(MidElement > -1)
7852  {
7854  }
7855  if(LagElement > -1)
7856  {
7858  }
7859  }
7860  int TrainDataEntryValue = Utilities->LoadFileInt(InFile);
7861 
7862  TrainDataEntryPtr = &(TrainController->TrainDataVector.at(0)) + TrainDataEntryValue;
7863  int ActionVectorEntryValue = Utilities->LoadFileInt(InFile);
7864 
7865  ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0)) + ActionVectorEntryValue;
7866 
7867  // need to set the TrainID if arriving at a continuation but hasn't been plotted yet
7868  if(LeadElement > -1)
7869  // need to include this in case train exiting & no lead element
7870  {
7872  {
7873  Track->TrackElementAt(668, LeadElement).TrainIDOnElement = TrainID; // no need to stop gap flashing if a continuation
7874  }
7875  }
7876  AValue = sqrt(2 * PowerAtRail / Mass);
7877 
7878  AnsiString LocationAndMarker = Utilities->LoadFileString(InFile);
7879 
7880  // possible RestoreTimetableLocation + Marker, where Marker is
7881  // "*****0" for !StoppedWithoutPower and "*****1" otherwise (from v2.4.0)
7882  // Note: including RestoreTimetableLocation with the marker is to correct an oversight - RestoreTimetableLocation should have been saved earlier
7883  // added at beta v0.2e
7884  if((LocationAndMarker[1] != '*') && (LocationAndMarker.Length() > 6))
7885  // name not allowed to include the '*' character
7886  {
7887  AnsiString Location = LocationAndMarker.SubString(1, LocationAndMarker.Length() - 6);
7888  bool GiveMessagesFalse = false;
7889  bool CheckLocationsExistInRailwayTrue = true;
7890  if(TrainController->CheckLocationValidity(3, Location, GiveMessagesFalse, CheckLocationsExistInRailwayTrue))
7891  {
7892  // otherwise take no action
7893  RestoreTimetableLocation = Location;
7894  }
7895  }
7896  AnsiString Marker = LocationAndMarker.SubString(LocationAndMarker.Length() - 5, 6);
7897 
7898  StoppedWithoutPower = false;
7899  if(Marker[6] == '1')
7900  {
7901  StoppedWithoutPower = true;
7902  }
7903  Utilities->CallLogPop(1458);
7904 }
7905 
7906 // ---------------------------------------------------------------------------
7907 
7908 bool TTrain::CheckOneSessionTrain(std::ifstream &InFile)
7909 {
7911  {
7912  return(false); // HeadCode
7913 
7914  }
7915  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7916  {
7917  return(false); // RearStartElement
7918 
7919  }
7920  if(!Utilities->CheckFileInt(InFile, 0, 3))
7921  {
7922  return(false); // RearStartExitPos
7923 
7924  }
7925  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7926  {
7927  return(false); // StartSpeed
7928 
7929  }
7930  if(!Utilities->CheckFileInt(InFile, 0, MaximumSpeedLimit))
7931  {
7932  return(false); // SignallerMaxSpeed
7933 
7934  }
7935  if(!Utilities->CheckFileBool(InFile))
7936  {
7937  return(false); // HoldAtLocationInTTMode
7938 
7939  }
7940  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7941  {
7942  return(false); // RepeatNumber (max 96 x 60 at 1 min intervals)
7943 
7944  }
7945  if(!Utilities->CheckFileInt(InFile, 0, 5760))
7946  {
7947  return(false); // IncrementalMinutes (max 96 x 60)
7948 
7949  }
7950  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
7951  {
7952  return(false); // IncrementalDigits
7953 
7954  }
7955  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7956  {
7957  return(false); // Mass
7958 
7959  }
7960  if(!Utilities->CheckFileInt(InFile, 0, 100000000))
7961  {
7962  return(false);
7963  }
7964  // FrontElementSpeedLimit - changed at v2.1.0 - effectively
7965  // not checked so as to allow for legacy session files, for new session files limit is set when loaded, see above
7966  if(!Utilities->CheckFileInt(InFile, 0, 10000000))
7967  {
7968  return(false); // FrontElementLength
7969 
7970  }
7971  if(!Utilities->CheckFileDouble(InFile))
7972  {
7973  return(false); // EntrySpeed
7974 
7975  }
7976  if(!Utilities->CheckFileDouble(InFile))
7977  {
7978  return(false); // ExitSpeedHalf
7979 
7980  }
7981  if(!Utilities->CheckFileDouble(InFile))
7982  {
7983  return(false); // ExitSpeedFull
7984 
7985  }
7986  if(!Utilities->CheckFileDouble(InFile))
7987  {
7988  return(false); // TimetableMaxRunningSpeed
7989 
7990  }
7991  if(!Utilities->CheckFileDouble(InFile))
7992  {
7993  return(false); // MaxRunningSpeed
7994 
7995  }
7996  if(!Utilities->CheckFileDouble(InFile))
7997  {
7998  return(false); // MaxExitSpeed
7999 
8000  }
8001  if(!Utilities->CheckFileDouble(InFile))
8002  {
8003  return(false); // MaxBrakeRate
8004 
8005  }
8006  if(!Utilities->CheckFileDouble(InFile))
8007  {
8008  return(false); // BrakeRate
8009 
8010  }
8011  if(!Utilities->CheckFileDouble(InFile))
8012  {
8013  return(false); // PowerAtRail
8014 
8015  }
8016  if(!Utilities->CheckFileBool(InFile))
8017  {
8018  return(false); // FirstHalfMove
8019 
8020  }
8021  if(!Utilities->CheckFileBool(InFile))
8022  {
8023  return(false); // OneLengthAccelDecel
8024 
8025  }
8026  if(!Utilities->CheckFileDouble(InFile))
8027  {
8028  return(false); // double(EntryTime)
8029 
8030  }
8031  if(!Utilities->CheckFileDouble(InFile))
8032  {
8033  return(false); // double(ExitTimeHalf)
8034 
8035  }
8036  if(!Utilities->CheckFileDouble(InFile))
8037  {
8038  return(false); // double(ExitTimeFull)
8039 
8040  }
8041  if(!Utilities->CheckFileDouble(InFile))
8042  {
8043  return(false); // double(ReleaseTime)
8044 
8045  }
8046  if(!Utilities->CheckFileDouble(InFile))
8047  {
8048  return(false); // double(TRSTime)
8049 
8050  }
8051  if(!Utilities->CheckFileDouble(InFile))
8052  {
8053  return(false); // double(LastActionTime)
8054 
8055  }
8056  if(!Utilities->CheckFileBool(InFile))
8057  {
8058  return(false); // CallingOnFlag
8059 
8060  }
8061  if(!Utilities->CheckFileBool(InFile))
8062  {
8063  return(false); // BeingCalledOn
8064 
8065  }
8066  if(!Utilities->CheckFileBool(InFile))
8067  {
8068  return(false); // DepartureTimeSet
8069 
8070  }
8071  if(!Utilities->CheckFileInt(InFile, 0, 2))
8072  {
8073  return(false); // (short)TrainMode
8074 
8075  }
8076  if(!Utilities->CheckFileBool(InFile))
8077  {
8078  return(false); // TimetableFinished
8079 
8080  }
8081  if(!Utilities->CheckFileBool(InFile))
8082  {
8083  return(false); // LastActionDelayFlag
8084 
8085  }
8086  if(!Utilities->CheckFileBool(InFile))
8087  {
8088  return(false); // SignallerRemoved
8089 
8090  }
8091  if(!Utilities->CheckFileBool(InFile))
8092  {
8093  return(false); // TerminatedMessageSent
8094 
8095  }
8096  if(!Utilities->CheckFileBool(InFile))
8097  {
8098  return(false); // Derailed
8099 
8100  }
8101  if(!Utilities->CheckFileBool(InFile))
8102  {
8103  return(false); // DerailPending
8104 
8105  }
8106  if(!Utilities->CheckFileBool(InFile))
8107  {
8108  return(false); // Crashed
8109 
8110  }
8111  if(!Utilities->CheckFileBool(InFile))
8112  {
8113  return(false); // StoppedAtBuffers
8114 
8115  }
8116  if(!Utilities->CheckFileBool(InFile))
8117  {
8118  return(false); // StoppedAtSignal
8119 
8120  }
8121  if(!Utilities->CheckFileBool(InFile))
8122  {
8123  return(false); // StoppedAtLocation
8124 
8125  }
8126  if(!Utilities->CheckFileBool(InFile))
8127  {
8128  return(false); // SignallerStopped
8129 
8130  }
8131  if(!Utilities->CheckFileBool(InFile))
8132  {
8133  return(false); // StoppedAfterSPAD
8134 
8135  }
8136  if(!Utilities->CheckFileBool(InFile))
8137  {
8138  return(false); // StoppedForTrainInFront
8139 
8140  }
8141  if(!Utilities->CheckFileBool(InFile))
8142  {
8143  return(false); // NotInService
8144 
8145  }
8146  if(!Utilities->CheckFileBool(InFile))
8147  {
8148  return(false); // Plotted
8149 
8150  }
8151  if(!Utilities->CheckFileBool(InFile))
8152  {
8153  return(false); // TrainGone
8154 
8155  }
8156  if(!Utilities->CheckFileBool(InFile))
8157  {
8158  return(false); // SPADFlag
8159 
8160  }
8161  if(!Utilities->CheckFileBool(InFile))
8162  {
8163  return(false); // TimeTimeLocArrived
8164 
8165  }
8166  if(!Utilities->CheckFileInt(InFile, 0, 15))
8167  {
8168  return(false); // HOffset[0]
8169 
8170  }
8171  if(!Utilities->CheckFileInt(InFile, 0, 15))
8172  {
8173  return(false); // HOffset[1]
8174 
8175  }
8176  if(!Utilities->CheckFileInt(InFile, 0, 15))
8177  {
8178  return(false); // HOffset[2]
8179 
8180  }
8181  if(!Utilities->CheckFileInt(InFile, 0, 15))
8182  {
8183  return(false); // HOffset[3]
8184 
8185  }
8186  if(!Utilities->CheckFileInt(InFile, 0, 15))
8187  {
8188  return(false); // VOffset[0]
8189 
8190  }
8191  if(!Utilities->CheckFileInt(InFile, 0, 15))
8192  {
8193  return(false); // VOffset[1]
8194 
8195  }
8196  if(!Utilities->CheckFileInt(InFile, 0, 15))
8197  {
8198  return(false); // VOffset[2]
8199 
8200  }
8201  if(!Utilities->CheckFileInt(InFile, 0, 15))
8202  {
8203  return(false); // VOffset[3]
8204 
8205  }
8206  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8207  {
8208  return(false); // PlotElement[0]
8209 
8210  }
8211  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8212  {
8213  return(false); // PlotElement[1]
8214 
8215  }
8216  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8217  {
8218  return(false); // PlotElement[2]
8219 
8220  }
8221  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8222  {
8223  return(false); // PlotElement[3]
8224 
8225  }
8226  if(!Utilities->CheckFileInt(InFile, 0, 3))
8227  {
8228  return(false); // PlotEntryPos[0]
8229 
8230  }
8231  if(!Utilities->CheckFileInt(InFile, 0, 3))
8232  {
8233  return(false); // PlotEntryPos[1]
8234 
8235  }
8236  if(!Utilities->CheckFileInt(InFile, 0, 3))
8237  {
8238  return(false); // PlotEntryPos[2]
8239 
8240  }
8241  if(!Utilities->CheckFileInt(InFile, 0, 3))
8242  {
8243  return(false); // PlotEntryPos[3]
8244 
8245  }
8246  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8247  {
8248  return(false); // TrainCrashedInto
8249 
8250  }
8251  if(!Utilities->CheckFileInt(InFile, 0, 2))
8252  {
8253  return(false); // (short)Straddle
8254 
8255  }
8256  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8257  {
8258  return(false); // NextTrainID
8259 
8260  }
8261  if(!Utilities->CheckFileInt(InFile, 0, 1000000))
8262  {
8263  return(false); // TrainID
8264 
8265  }
8266  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8267  {
8268  return(false); // LeadElement
8269 
8270  }
8271  if(!Utilities->CheckFileInt(InFile, 0, 3))
8272  {
8273  return(false); // LeadEntryPos
8274 
8275  }
8276  if(!Utilities->CheckFileInt(InFile, 0, 3))
8277  {
8278  return(false); // LeadExitPos
8279 
8280  }
8281  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8282  {
8283  return(false); // MidElement
8284 
8285  }
8286  if(!Utilities->CheckFileInt(InFile, 0, 3))
8287  {
8288  return(false); // MidEntryPos
8289 
8290  }
8291  if(!Utilities->CheckFileInt(InFile, 0, 3))
8292  {
8293  return(false); // MidExitPos
8294 
8295  }
8296  if(!Utilities->CheckFileInt(InFile, -1, 1000000))
8297  {
8298  return(false); // LagElement
8299 
8300  }
8301  if(!Utilities->CheckFileInt(InFile, 0, 3))
8302  {
8303  return(false); // LagEntryPos
8304 
8305  }
8306  if(!Utilities->CheckFileInt(InFile, 0, 3))
8307  {
8308  return(false); // LagExitPos
8309 
8310  }
8311  if(!Utilities->CheckFileInt(InFile, 0, 14))
8312  {
8313  return(false);
8314  }
8315  // Background colour number //14 is failed colour at v2.4.0
8316  if(!Utilities->CheckFileBool(InFile))
8317  {
8318  return(false); // ForwardHeadCode
8319 
8320  }
8321  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8322  {
8323  return(false); // TrainDataEntryValue
8324 
8325  }
8326  if(!Utilities->CheckFileInt(InFile, 0, 10000))
8327  {
8328  return(false); // ActionVectorEntryValue
8329 
8330  }
8332  {
8333  return(false); // End of train marker + possible RestoreTimetableLocation
8334 
8335  }
8336  // and StoppedWithoutPower flag
8337  return(true);
8338 }
8339 
8340 // ---------------------------------------------------------------------------
8341 
8342 void TTrain::PlotTrainInZoomOutMode(int Caller, bool Flash)
8343 {
8344  // order below reflects significance so earlier shows first, as may have more than one flag set
8345  // only plot flashing trains when Flash is true
8346 
8347 /*
8348  clCrashedBackground (TColor)0x0000FF red
8349  clDerailedBackground (TColor)0x0000FF red
8350  clSPADBackground (TColor)0x00FFFF yellow
8351  clTrainFailedBackground (TColor)0x0066FF orange new at v2.4.0
8352  clCallOnBackground (TColor)0xFF33FF light magenta
8353  clSignalStopBackground (TColor)0x00FF66 green
8354  clBufferAttentionNeeded (TColor)0xFFFF00 cyan
8355  clStationStopBackground (TColor)0xCCFFCC pale green
8356  clTRSBackground (TColor)0xFFCCFF light pink
8357  clBufferStopBackground (TColor)0xFFFFCC pale cyan
8358  clStoppedTrainInFront (TColor)0xFF9999 lavender blue
8359  clSignallerStopped (TColor)0x99CCFF caramel
8360  clNormalBackground (TColor)0xCCCCCC grey
8361 */
8362 
8363  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainInZoomOutMode" + "," + HeadCode);
8364  bool HideFlashingTrain = true;
8365  // hide it when Flash false so it blinks on and off
8366  // if don't hide it it stays displayed all the time
8367  Graphics::TBitmap *SmallTrainBitmap;
8368 
8369  // NB ensure retain same order as zoomed in order so colours correspond
8371  {
8372  TrainController->CrashWarning = true;
8373  SmallTrainBitmap = RailGraphics->smRed;
8374  }
8376  {
8378  SmallTrainBitmap = RailGraphics->smRed;
8379  }
8381  {
8382  TrainController->SPADWarning = true;
8383  SmallTrainBitmap = RailGraphics->smYellow;
8384  }
8386  {
8388  SmallTrainBitmap = RailGraphics->smOrange;
8389  }
8391  {
8393  SmallTrainBitmap = RailGraphics->smMagenta;
8394  }
8396  {
8398  SmallTrainBitmap = RailGraphics->smBrightGreen;
8399  }
8401  {
8403  SmallTrainBitmap = RailGraphics->smCyan;
8404  }
8406  {
8407  SmallTrainBitmap = RailGraphics->smPaleGreen;
8408  HideFlashingTrain = false;
8409  }
8411  {
8412  SmallTrainBitmap = RailGraphics->smCyan;
8413  HideFlashingTrain = false;
8414  }
8416  {
8417  SmallTrainBitmap = RailGraphics->smLightBlue;
8418  HideFlashingTrain = false;
8419  }
8421  {
8422  SmallTrainBitmap = RailGraphics->smCaramel;
8423  HideFlashingTrain = false;
8424  }
8425  else
8426  {
8427  SmallTrainBitmap = RailGraphics->smBlack; // moving
8428  HideFlashingTrain = false;
8429  }
8430  // now plot the new train
8431  // just plot lead & mid, unless lead == -1 in which case plot mid & lag
8432  if((LeadElement > -1) && (!HideFlashingTrain || Flash))
8433  {
8434  Display->PlotSmallOutput(4, Track->TrackElementAt(441, LeadElement).HLoc * 4, Track->TrackElementAt(442, LeadElement).VLoc * 4, SmallTrainBitmap);
8435  }
8436  if((MidElement > -1) && (!HideFlashingTrain || Flash))
8437  {
8438  Display->PlotSmallOutput(5, Track->TrackElementAt(443, MidElement).HLoc * 4, Track->TrackElementAt(444, MidElement).VLoc * 4, SmallTrainBitmap);
8439  }
8440  if((LeadElement == -1) && (LagElement > -1) && (!HideFlashingTrain || Flash))
8441  {
8442  Display->PlotSmallOutput(6, Track->TrackElementAt(445, LagElement).HLoc * 4, Track->TrackElementAt(446, LagElement).VLoc * 4, SmallTrainBitmap);
8443  }
8447  Utilities->CallLogPop(1459);
8448 }
8449 
8450 // ---------------------------------------------------------------------------
8451 
8453 {
8454  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrainInZoomOutMode," + AnsiString(TrainID) + "," + HeadCode);
8455  if(!Display->ZoomOutFlag)
8456  {
8457  Utilities->CallLogPop(1304);
8458  return;
8459  }
8460  for(int y = 0; y < 3; y++)
8461  {
8462  if(OldZoomOutElement[y] > -1)
8463  {
8464  bool FoundFlag = false;
8465  TTrackElement ATElement = Track->TrackElementAt(717, OldZoomOutElement[y]);
8466  TTrackElement IATElement1, IATElement2;
8467  // default elements to begin with
8468  Display->PlotSmallOutput(7, ATElement.HLoc * 4, ATElement.VLoc * 4, RailGraphics->smSolidBgnd); // plot the blank
8469  TTrack::TIMPair IMPair = Track->GetVectorPositionsFromInactiveTrackMap(14, ATElement.HLoc, ATElement.VLoc, FoundFlag);
8470  // Note, have to plot inactives before track because track has to overwrite NamedLocationElements
8471  if(FoundFlag)
8472  {
8473  IATElement1 = Track->InactiveTrackElementAt(87, IMPair.first);
8474  Display->PlotSmallOutput(8, IATElement1.HLoc * 4, IATElement1.VLoc * 4, IATElement1.SmallGraphicPtr);
8475  if(IMPair.first != IMPair.second)
8476  {
8477  IATElement2 = Track->InactiveTrackElementAt(88, IMPair.second);
8478  Display->PlotSmallOutput(9, IATElement2.HLoc * 4, IATElement2.VLoc * 4, IATElement2.SmallGraphicPtr);
8479  }
8480  }
8481  Display->PlotSmallOutput(10, ATElement.HLoc * 4, ATElement.VLoc * 4, ATElement.SmallGraphicPtr);
8482  }
8483  }
8484  Utilities->CallLogPop(1305);
8485 }
8486 
8487 // ---------------------------------------------------------------------------
8488 
8489 bool TTrain::TrainAtLocation(int Caller, AnsiString &LocationName)
8490 {
8491  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainAtLocation" + "," + HeadCode);
8492  LocationName = "";
8493  if(!RevisedStoppedAtLoc())
8494  {
8495  Utilities->CallLogPop(1398);
8496  return(false);
8497  }
8498  if(LeadElement > -1)
8499  {
8501  }
8502  if((LocationName == "") && (MidElement > -1))
8503  {
8504  LocationName = Track->TrackElementAt(682, MidElement).ActiveTrackElementName;
8505  }
8506  if((LocationName == "") && (LagElement > -1))
8507  {
8508  LocationName = Track->TrackElementAt(683, LagElement).ActiveTrackElementName;
8509  }
8510  if(LocationName == "")
8511  {
8512  throw Exception("Error - Location name not set in TrainAtLocation");
8513  }
8514  Utilities->CallLogPop(1399);
8515  return(true);
8516 }
8517 
8518 // ---------------------------------------------------------------------------
8519 
8520 void TTrain::PlotTrain(int Caller, TDisplay *Disp)
8521 {
8522  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrain" + "," + HeadCode);
8523  for(int x = 0; x < 4; x++)
8524  {
8525  PlotTrainGraphic(7, x, Disp);
8526  }
8527  Utilities->CallLogPop(647);
8528 }
8529 
8530 // ---------------------------------------------------------------------------
8531 
8532 void TTrain::WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
8533 {
8534  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainToImage" + "," + HeadCode);
8535  for(int x = 0; x < 4; x++)
8536  {
8537  if(PlotElement[x] > -1)
8538  {
8539  Bitmap->Canvas->Draw(((Track->TrackElementAt(744, PlotElement[x]).HLoc - Track->GetHLocMin()) * 16 + HOffset[x]),
8540  ((Track->TrackElementAt(745, PlotElement[x]).VLoc - Track->GetVLocMin()) * 16 + VOffset[x]), HeadCodePosition[x]);
8541  }
8542  }
8543  Utilities->CallLogPop(1708);
8544 }
8545 
8546 // ---------------------------------------------------------------------------
8547 
8548 bool TTrain::LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber) // added at v1.2.0
8549 {
8550  // return true for any part of train occupying LinkNumber at TrackVectorPosition, false for anything else, including no LinkNumber & no TrackVectorPosition
8551  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LinkOccupied," + AnsiString(TrackVectorPosition) + "," +
8552  AnsiString(LinkNumber) + "," + HeadCode);
8553 
8554 /* Note on Straddle: Straddle defines the actual train position wrt Lag, Mid & Lead elements at all times other than within UpdateTrain. Is only MidLag outside UpdateTrain
8555  on first entry at a continuation (with no train plotted), and that has no relevance here. In all other cases it is either LeadMid (when train fully
8556  on Lead & Mid elements) or LeadMidLag (when train straddling 3 elements).
8557 */
8558 
8559  // note that MidElement always fully occupied
8560  if((MidElement == TrackVectorPosition) && ((Track->TrackElementAt(883, TrackVectorPosition).Link[MidEntryPos] == LinkNumber) || (Track->TrackElementAt(884,
8561  TrackVectorPosition).Link[MidExitPos] == LinkNumber)))
8562  {
8563  Utilities->CallLogPop(2005);
8564  return(true);
8565  }
8566  if(Straddle == LeadMid)
8567  {
8568  if((LeadElement == TrackVectorPosition) && ((Track->TrackElementAt(885, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber) ||
8569  (Track->TrackElementAt(886, TrackVectorPosition).Link[LeadExitPos] == LinkNumber)))
8570  {
8571  Utilities->CallLogPop(2006);
8572  return(true);
8573  }
8574  }
8575  else if(Straddle == LeadMidLag)
8576  {
8577  if((LeadElement == TrackVectorPosition) && (Track->TrackElementAt(887, TrackVectorPosition).Link[LeadEntryPos] == LinkNumber))
8578  // only interested in LeadEntryPos as train not occupying ExitPos yet
8579  {
8580  Utilities->CallLogPop(2007);
8581  return(true);
8582  }
8583  else if((LagElement == TrackVectorPosition) && (Track->TrackElementAt(888, TrackVectorPosition).Link[LagExitPos] == LinkNumber))
8584  // only interested in LagExitPos as train has left EntryPos
8585  {
8586  Utilities->CallLogPop(2008);
8587  return(true);
8588  }
8589  }
8590  Utilities->CallLogPop(2009);
8591  return(false);
8592 }
8593 
8594 // ---------------------------------------------------------------------------
8595 
8596 float TTrain::CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair) // only called for running trains.
8601 // TimeToExit & ExitPair added for multiplayer
8602 {
8603  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcTimeToAct, " + HeadCode);
8604  int DistanceToRedSignal = 0, DistanceToExit = -1;
8605  float TimeToAct = 0, LastTimeToExit = TimeToExit;
8606  TimeToExit = -1;
8607  ExitPair.first = -1;
8608  ExitPair.second = -1;
8609  float MinsEarly = 0; //added at v2.6.1
8610  TDateTime DepartureTime; //added at v2.6.1 //ArrivalTime used instead of this at v2.9.0 but still calculate it in case need it later for some reason
8611  TDateTime ArrivalTime; //added at v2.9.0 as MinsEarly used DepartureTime which wasn't correct
8612  float TempTTE;
8613 
8614  if(TrainFailed)
8615  {
8616  Utilities->CallLogPop(2147);
8617  return(0); // time to act now, time to exit set above
8618  }
8619  if(SignallerStopped)
8620  {
8621  Utilities->CallLogPop(2080);
8622  return(-1); //time to exit set above
8623  }
8624  if(BeingCalledOn) //added at v2.7.0 so zero time to act cancelled right away
8625  {
8626  Utilities->CallLogPop(2266);
8627  return(-1); // time to exit set above
8628  }
8629 
8630  // check if if exiting at a continuation, if so there's no action time needed but still need exit time & ExitPair
8631  if((LeadElement == -1) && (MidElement == -1) && (LagElement > -1) && (Track->TrackElementAt(1411, LagElement).TrackType == Continuation)
8632  /*&& (ExitSpeedFull > 1)*/) //LagElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8633  {
8634  if(Straddle == LeadMidLag) //only half of rear train element on exit, 0.5 lengths to exit
8635  {
8636  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8637  {
8638  TempTTE = (0.5 * Track->TrackElementAt(1412, LagElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8639  if(TempTTE < LastTimeToExit)
8640  {
8641  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8642  }
8643  else
8644  {
8645  TimeToExit = LastTimeToExit;
8646  }
8647  }
8648  else
8649  {
8650  TimeToExit = LastTimeToExit;
8651  }
8652  ExitPair.first = Track->TrackElementAt(1413, LagElement).HLoc;
8653  ExitPair.second = Track->TrackElementAt(1414, LagElement).VLoc;
8654  Utilities->CallLogPop(2342);
8655  return(-1);
8656  }
8657  else
8658  {
8659  TimeToExit = 0; //all train exited
8660  ExitPair.first = Track->TrackElementAt(1415, LagElement).HLoc;
8661  ExitPair.second = Track->TrackElementAt(1416, LagElement).VLoc;
8662  Utilities->CallLogPop(2343);
8663  return(-1);
8664  }
8665  }
8666  if((LeadElement == -1) && (MidElement > -1) && (Track->TrackElementAt(1417, MidElement).TrackType == Continuation)/* && (ExitSpeedFull > 1)*/)
8667  //here MidElement is the exit //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8668  {
8669  if(Straddle == LeadMidLag) //front element of train half off the exit, 1.5 lengths to exit
8670  {
8671  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8672  {
8673  TempTTE = (1.5 * Track->TrackElementAt(1418, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8674  if(TempTTE < LastTimeToExit)
8675  {
8676  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8677  }
8678  else
8679  {
8680  TimeToExit = LastTimeToExit;
8681  }
8682  }
8683  else
8684  {
8685  TimeToExit = LastTimeToExit;
8686  }
8687  ExitPair.first = Track->TrackElementAt(1419, MidElement).HLoc;
8688  ExitPair.second = Track->TrackElementAt(1420, MidElement).VLoc;
8689  Utilities->CallLogPop(2344);
8690  return(-1);
8691  }
8692  else //front element of train fully off the exit, one length to exit
8693  {
8694  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8695  {
8696  TempTTE = (Track->TrackElementAt(1421, MidElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8697  if(TempTTE < LastTimeToExit)
8698  {
8699  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8700  }
8701  else
8702  {
8703  TimeToExit = LastTimeToExit;
8704  }
8705  }
8706  else
8707  {
8708  TimeToExit = LastTimeToExit;
8709  }
8710  ExitPair.first = Track->TrackElementAt(1422, MidElement).HLoc;
8711  ExitPair.second = Track->TrackElementAt(1423, MidElement).VLoc;
8712  Utilities->CallLogPop(2345);
8713  return(-1);
8714  }
8715  }
8716  if(LeadElement > -1)
8717  {
8719  /* && (ExitSpeedFull > 1)*/) //LeadElement is the exit. If LeadMidLag have lead half on exit or if LeadMid have lead full on exit
8720  { //if LeadMidLag have 2.5 lengths to exit, else have 2 lengths to exit
8721  //ExitSpeedFull removed at v2.12.0 because of Cameron Neasom's error 28/01/22 - braking as entered continuation
8722  if(Straddle == LeadMidLag)
8723  {
8724  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8725  {
8726  TempTTE = (2.5 * Track->TrackElementAt(1426, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8727  if(TempTTE < LastTimeToExit)
8728  {
8729  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8730  }
8731  else
8732  {
8733  TimeToExit = LastTimeToExit;
8734  }
8735  }
8736  else
8737  {
8738  TimeToExit = LastTimeToExit;
8739  }
8740  ExitPair.first = Track->TrackElementAt(1427, LeadElement).HLoc;
8741  ExitPair.second = Track->TrackElementAt(1428, LeadElement).VLoc;
8742  Utilities->CallLogPop(2346);
8743  return(-1);
8744  }
8745  else
8746  {
8747  if(ExitSpeedFull > 0) //added at v2.12.0 because of Cameron Neasom's error 28/01/22
8748  {
8749  TempTTE = (2 * Track->TrackElementAt(1429, LeadElement).Length01) * 3.6 / 60 / ExitSpeedFull;
8750  if(TempTTE < LastTimeToExit)
8751  {
8752  TimeToExit = TempTTE; //else leave as is, don't want a sudden increase in time
8753  }
8754  else
8755  {
8756  TimeToExit = LastTimeToExit;
8757  }
8758  }
8759  else
8760  {
8761  TimeToExit = LastTimeToExit;
8762  }
8763  ExitPair.first = Track->TrackElementAt(1430, LeadElement).HLoc;
8764  ExitPair.second = Track->TrackElementAt(1431, LeadElement).VLoc;
8765  Utilities->CallLogPop(2347);
8766  return(-1);
8767  }
8768  }
8769  }
8770 //here if LeadElement > -1 and its forward connection also > -1
8771 
8772  // calc distance to next red signal
8773  if(!Stopped() || RevisedStoppedAtLoc())
8774  {
8775  int FirstPosToBeMeasured = Track->TrackElementAt(953, LeadElement).Conn[LeadExitPos];
8776  int FirstEntryPos = Track->TrackElementAt(954, LeadElement).ConnLinkPos[LeadExitPos];
8777  if((Straddle == LeadMidLag) && (TrainMode == Timetable))
8778 /* In TTMode it's important to set the first element to be measured ahead of the lead element only when the train fully on
8779  2 elements. Otherwise, if the train is only half on the lead element and approaching a station stop where the platform doesn't
8780  extend beyond the lead element stop point, the element ahead of the lead element is not a location whereas the ActionVector
8781  still points to the station stop location. In these circumstances the train hasn't yet stopped, so the dwell time at the
8782  stop isn't calculated, and the station to be stopped at isn't found as a future stop and nor are any other future stops
8783  because the ActionVector name never matches a future station. Hence all dwell times are omitted until the train lands fully
8784  on two elements. To avoid this when Straddle is LeadMidLag the first element to be measured is set to the lead element, so
8785  before the train has stopped the current station is still recognised as a future stop.
8786  In signaller mode stops don't count, and if pass red signal command is given then when have LeadMidLag the current element
8787  becomes the signal, and the time to act indication becomes 'NOW'.
8788 */
8789  {
8790  FirstPosToBeMeasured = LeadElement;
8791  FirstEntryPos = LeadEntryPos;
8792  }
8793  float CurrentStopTime; // set to 0 at start of function
8794  float LaterStopTime; // set to 0 at start of function
8795  float RecoverableTime; // set to 0 at start of function
8796  int AvTrackSpeed; // set to zero at start of function
8797  bool SigControlAndCanPassRedSignal = ((TrainMode == Signaller) && AllowedToPassRedSignal);
8798  DistanceToRedSignal = TrainController->CalcDistanceToRedSignalandStopTime(0, FirstPosToBeMeasured, FirstEntryPos, SigControlAndCanPassRedSignal,
8799  ActionVectorEntryPtr, HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
8800 //at this point can't have both DistanceToRedSignal and DistanceToExit both set. Either both will be unset (-1) or one will be set.
8801 //Therefore since need to calculate the time for each in the same way use a generic
8802 
8803  if((DistanceToRedSignal == -1) && (DistanceToExit == -1))// both unset so no action needed
8804  {
8805  TimeToExit = -1;
8806  Utilities->CallLogPop(2076);
8807  return(-1);
8808  }
8809 //else one or other is set
8810  bool DistanceToRedSignalSet = (DistanceToRedSignal > -1);
8811  bool DistanceToExitSet = (DistanceToExit > -1);
8812  int GenericDistance = DistanceToRedSignal;
8813  if(DistanceToExitSet)
8814  {
8815  GenericDistance = DistanceToExit;
8816  }
8817 /* Have MinsDelayed; pos or neg,
8818  CurrentStopTime; pos or zero
8819  LaterStopTime; pos or zero
8820  RecoverableTime; pos or zero
8821 
8822  & from these calculate TotalStopTime. noting that:
8823  If stopped CurrentStopTime automatically adjusts for all early running and for as much late running as possible
8824  RecoverableTime always < LaterStopTime or both zero
8825  can't subtract more than RecoverableTime (MinsDelayed > 0)
8826  only subtract from LaterStopTime, not CurrentTime (MinsDelayed > 0)
8827  only subtract from LaterStopTime if LaterStopTime > 0 (MinsDelayed > 0)
8828  only add to LaterStopTime if LaterStopTime > 0 (MinsDelayed < 0)
8829  if running early & stopped at location CurrentStopTime will automatically include the excess
8830 */
8831  float TimeToSubtract, TotalStopTime;
8832  if(MinsDelayed > RecoverableTime)
8833  {
8834  TimeToSubtract = RecoverableTime;
8835  }
8836  else
8837  {
8838  TimeToSubtract = MinsDelayed; // may be negative;
8839  }
8840  if((AvTrackSpeed > 0) && (DistanceToStationStop <= GenericDistance) && (DistanceToStationStop > 0)) //protection against div by zero, not needed of no stop
8841  //before red signal, DistanceToStationStop != 0 as set to 0 if invalid
8842  //added at v2.6.1, DistanceToStationStop is calculated in SetTrainMovementValues, AvTrackSpeed is average to next red signal, but should be ok to use for
8843  //next station stop
8844  //after 2.7.0 changed (DistanceToStationStop < DistanceToRedSignal) to (DistanceToStationStop <= DistanceToRedSignal) because often have departure signal
8845  //next to the stop platform and if so the two distances are the same and the station stop time isn't included. Also after 2.7.0 used GetRepeatTime... to calc
8846  //departure time because ActionVectorEntryPtr->DepartureTime is the base time and therefore incorrect for repeats.
8847  //first find departure time from the next stop
8848  //at v2.9.0 changed MinsEarly calc to use ArrivalTime instead of departure time
8849  {
8850  if(ActionVectorEntryPtr->FormatType == TimeTimeLoc) //if already arrived then MinsEarly will be < 0 so becomes set to 0
8851  {
8854  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8855  }
8856  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime != TDateTime(-1))) // not arrived yet
8857  {
8859  MinsEarly = (double(ArrivalTime - TrainController->TTClockTime) * 86400 / 60) - (DistanceToStationStop * 3.6 / 60 / AvTrackSpeed);
8860  if((ActionVectorEntryPtr + 1)->FormatType == TimeLoc)
8861  {
8862  // must be a departure
8863  DepartureTime = TrainController->GetRepeatTime(70, (ActionVectorEntryPtr + 1)->DepartureTime, RepeatNumber, IncrementalMinutes);
8864  }
8865  }
8866  else if((ActionVectorEntryPtr->FormatType == TimeLoc) && (ActionVectorEntryPtr->ArrivalTime == TDateTime(-1))) //already arrived
8867  {
8868  MinsEarly = 0;
8869  }
8870  if(MinsEarly < 0)
8871  {
8872  MinsEarly = 0;
8873  }
8874  }
8875  if(MinsDelayed < 0) // MinsDelayed < 0 means have arrived early at a station
8876  {
8877  if(CurrentStopTime > 0)
8878  {
8879  TotalStopTime = CurrentStopTime + LaterStopTime;
8880  }
8881  // stopped at loc, will depart on time
8882  else
8883  {
8884  TotalStopTime = LaterStopTime - MinsDelayed;
8885  }
8886  // not stopped, will depart on time at first later stop so add the delay
8887  }
8888  else if((MinsEarly > 0) && !Stopped()) //running early
8889  {
8890  TotalStopTime = LaterStopTime + MinsEarly;
8891  }
8892  else // on time or running late
8893  {
8894  if(LaterStopTime == 0)
8895  {
8896  TotalStopTime = CurrentStopTime;
8897  }
8898  // no later stops, if stopped now will depart as soon as possible,
8899  // if not stopped no stop times to add
8900  else
8901  {
8902  TotalStopTime = CurrentStopTime + LaterStopTime - TimeToSubtract; // later stops so deduct as much as can
8903  }
8904  }
8905  if(AvTrackSpeed < 30)
8906  {
8907  AvTrackSpeed = 30;
8908  }
8909  int Speed = AvTrackSpeed;
8910  if(AvTrackSpeed > int(MaxRunningSpeed))
8911  {
8912  Speed = int(MaxRunningSpeed);
8913  }
8914  if(TrainMode == Signaller)
8915  {
8916  Speed = SignallerMaxSpeed;
8917  TotalStopTime = 0;
8918  }
8919  if(DistanceToRedSignalSet)
8920  {
8921  TimeToAct = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8922  // accel & decel taken into account in
8923  // CalcDistanceToRedSignalandStopTime
8924  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8925  TimeToExit = -1;
8926  Utilities->CallLogPop(2079);
8927  return(TimeToAct);
8928  }
8929  else //DistanceToExitSet must be true
8930  {
8931  TimeToExit = TotalStopTime + GenericDistance * 3.6 / 60 / Speed;
8932  // accel & decel taken into account in
8933  // CalcDistanceToRedSignalandStopTime
8934  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
8935  Utilities->CallLogPop(2370);
8936  return(-1); //no red signal so no time to act
8937  }
8938  }
8939  else // stopped not at location
8940  {
8942  {
8943  TimeToAct = 0.0;
8944  TimeToExit = -1;
8945  }
8946  // but if stopped at a signal & autosigs route after it then ok
8947  if(StoppedAtSignal)
8948  {
8949  int NextElement = Track->TrackElementAt(928, LeadElement).Conn[LeadExitPos];
8950  int NextEntryPos = Track->TrackElementAt(929, LeadElement).ConnLinkPos[LeadExitPos];
8951  int NextExitPos;
8952  if(Track->TrackElementAt(930, NextElement).TrackType == Points)
8953  {
8954  if((NextEntryPos == 0) || (NextEntryPos == 2))
8955  // leading entry point
8956  {
8957  if(Track->TrackElementAt(931, NextElement).Attribute == 0)
8958  {
8959  NextExitPos = 1;
8960  }
8961  else
8962  {
8963  NextExitPos = 3;
8964  }
8965  }
8966  else
8967  {
8968  NextExitPos = 0; // trailing entry point
8969  }
8970  }
8971  else
8972  {
8973  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
8974  }
8975  int NextButOneElement = Track->TrackElementAt(932, NextElement).Conn[NextExitPos];
8976  int NextButOneEntryPos = Track->TrackElementAt(933, NextElement).ConnLinkPos[NextExitPos];
8977  int RouteNumber; // holder for referenced value, not used
8978  if(AllRoutes->GetRouteTypeAndNumber(32, NextButOneElement, NextButOneEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
8979  {
8980  TimeToAct = -1;
8981  TimeToExit = -1;
8982  }
8983  }
8984  Utilities->CallLogPop(2074);
8985  return(TimeToAct);
8986  }
8987 }
8988 
8989 // ---------------------------------------------------------------------------
8990 
8992 {
8993  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainOnContinuation, " + HeadCode);
8994  if(LeadElement > -1)
8995  {
8997  {
8998  Utilities->CallLogPop(2148);
8999  return(true);
9000  }
9001  }
9002  if(MidElement > -1)
9003  {
9005  {
9006  Utilities->CallLogPop(2149);
9007  return(true);
9008  }
9009  }
9010  if(LagElement > -1)
9011  {
9013  {
9014  Utilities->CallLogPop(2150);
9015  return(true);
9016  }
9017  }
9018  Utilities->CallLogPop(2151);
9019  return(false);
9020 }
9021 
9022 // ---------------------------------------------------------------------------
9023 // TTrainController
9024 // ---------------------------------------------------------------------------
9025 
9027 {
9028  OnTimeArrivals = 0;
9029  LateArrivals = 0;
9030  EarlyArrivals = 0;
9031  OnTimePasses = 0;
9032  LatePasses = 0;
9033  EarlyPasses = 0;
9034  OnTimeExits = 0; //these 3 exits added at v2.9.2 - missed in error earlier
9035  LateExits = 0;
9036  EarlyExits = 0;
9037  OnTimeDeps = 0;
9038  LateDeps = 0;
9039  MissedStops = 0;
9040  OtherMissedEvents = 0;
9041  UnexpectedExits = 0;
9042  NumFailures = 0;
9043  IncorrectExits = 0;
9044  SPADEvents = 0;
9045  SPADRisks = 0;
9046  CrashedTrains = 0;
9047  Derailments = 0;
9048  TotArrDepPass = 0;
9049  TotLateArrMins = 0;
9050  TotEarlyArrMins = 0;
9051  TotLatePassMins = 0;
9052  TotEarlyPassMins = 0;
9053  TotLateExitMins = 0; //added at v2.9.1
9054  TotEarlyExitMins = 0; //added at v2.9.1
9055  TotLateDepMins = 0;
9056  ExcessLCDownMins = 0;
9057  SkippedTTEvents = 0; //added at v2.11.0
9058  TTClockTime = 0; // added for v0.6
9060  // added at v1.3.0 to ensure false at start
9061  OpTimeToActUpdateCounter = 0; // new v2.2.0
9062  OpActionPanelVisible = false; // new v2.2.0
9063  // reset all message flags, stops them being given twice (shouldn't be needed here but add for safety) //new at v2.4.0
9064  SSHigh = false;
9065  MRSHigh = false;
9066  MRSLow = false;
9067  MassHigh = false;
9068  BFHigh = false;
9069  BFLow = false;
9070  PwrHigh = false;
9071  SigSHigh = false;
9072  SigSLow = false;
9073  randomize();
9074  // to seed rand() & random() with a random number (see UpdateTrain)
9075 }
9076 
9077 // ---------------------------------------------------------------------------
9078 
9080 {
9081  for(unsigned int x = 0; x < TrainVector.size(); x++)
9082  {
9083  TrainVectorAt(32, x).DeleteTrain(4);
9084  }
9085  TrainVector.clear();
9086 }
9087 
9088 // ---------------------------------------------------------------------------
9089 
9090 void TTrainController::LogEvent(AnsiString Str)
9091 {
9092  AnsiString FullStr = Utilities->TimeStamp() + "," + TTClockTime.FormatString("hh:nn:ss") + "," + Str;
9093 
9094  // restrict to last 1000 entries
9095  Utilities->EventLog.push_back(FullStr);
9096  if(Utilities->EventLog.size() > 1000)
9097  {
9098  Utilities->EventLog.pop_front();
9099  }
9100 }
9101 
9102 // ---------------------------------------------------------------------------
9103 
9105 {
9106  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Operate");
9107  bool ClockState = Utilities->Clock2Stopped;
9108  Utilities->Clock2Stopped = true;
9109  // new section dealing with Snt & Snt-sh additions
9110  // BUT don't add trains if points or route flashing [conditions added for Version 0.6 as a result of Najamuddin's error - 15/01/11] - wait until next
9111  // clock tick after stops flashing
9113  {
9114  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9115  {
9116  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
9117  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
9118  TActionEventType EventType = NoEvent;
9119  if(AVEntry0.Command == "Snt")
9120  {
9121  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9122  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9123  int IncrementalMinutes = 0;
9124  int IncrementalDigits = 0;
9125  if(AVEntryLast.FormatType == Repeat)
9126  {
9127  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9128  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9129  }
9130  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9131  {
9132  throw Exception("Error - Repeat entry && less than two trains for Snt entry: " + TDEntry.HeadCode);
9133  }
9134  // see above note
9135 
9136  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9137  {
9138  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
9139  if(TTOD.RunningEntry != NotStarted)
9140  {
9141  continue;
9142  }
9143 
9144 //Multiplayer: here check for a train entering at a coupling {RearStartOrRepeatMins shows if it's a coupling or not), and can only be a Snt entry
9145 //if so and no arrival signalled yet bypass the timetabled arrival
9146 //if so and arrival signalled then start the new service, using the repeat number and headcode for the entering train
9147 //if a repeat is skipped then should be ok if it arrives later as its RunningEntry is still NotStarted
9148 
9149  if(GetRepeatTime(2, AVEntry0.EventTime, y, IncrementalMinutes) > TTClockTime)
9150  {
9151  break; // all the rest will also be greater
9152  }
9153  AnsiString TrainHeadCode = GetRepeatHeadCode(22, TDEntry.HeadCode, y, IncrementalDigits);
9154  if(AddTrain(2, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TrainHeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9155  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, y, IncrementalMinutes, IncrementalDigits,
9156  TDEntry.SignallerSpeed, AVEntry0.SignallerControl, EventType))
9157  {
9158  TTOD.TrainID = TrainVector.back().TrainID;
9159  TTOD.RunningEntry = Running;
9160  }
9161  else if(EventType == FailTrainEntry)
9162  {
9163  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9164  }
9165  }
9166  }
9167  if(AVEntry0.Command == "Snt-sh")
9168  // just start this once, shuttle repeats take care of restarts
9169  {
9170  // calc below only for Snt & Snt-sh entries rather than all entries to save time
9171  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
9172  int IncrementalMinutes = 0;
9173  int IncrementalDigits = 0;
9174  if(AVEntryLast.FormatType == Repeat)
9175  {
9176  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
9177  IncrementalDigits = AVEntryLast.FrontStartOrRepeatDigits;
9178  }
9179  if((AVEntryLast.FormatType == Repeat) && (TDEntry.NumberOfTrains < 2))
9180  {
9181  throw Exception("Error - Repeat entry && less than two trains for Snt-sh entry: " + TDEntry.HeadCode);
9182  }
9183  // see above note
9184  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(0);
9185  if(TTOD.RunningEntry == NotStarted)
9186  {
9187  if(AVEntry0.EventTime <= TTClockTime)
9188  {
9189  if(AddTrain(3, AVEntry0.RearStartOrRepeatMins, AVEntry0.FrontStartOrRepeatDigits, TDEntry.HeadCode, TDEntry.StartSpeed, TDEntry.Mass,
9190  TDEntry.MaxRunningSpeed, TDEntry.MaxBrakeRate, TDEntry.PowerAtRail, "Timetable", &TDEntry, 0, IncrementalMinutes, IncrementalDigits,
9191  TDEntry.SignallerSpeed, false, EventType))
9192  // false for SignallerControl
9193  {
9194  TTOD.TrainID = TrainVector.back().TrainID;
9195  TTOD.RunningEntry = Running;
9196  }
9197  else if(EventType == FailTrainEntry)
9198  {
9199  break; // if a train can't enter no point checking any more repeats as they won't be able to enter either
9200  }
9201  }
9202  }
9203  }
9204  }
9205  }
9206 
9207  // deal with running trains but abort if any vectors added, would probably be OK but don't risk a vector reallocation disrupting the
9208  // iteration, next cycle will catch up with any other pending updates
9209  if(!TrainVector.empty())
9210  {
9211  TrainAdded = false;
9212 
9213 //elapsed time investigations
9214 
9215 //elapsed time segment
9216 //double Start, End;
9217 //AnsiString ElapsedTimeReport = "";
9218 //end elasped time segment
9219  AllRoutes->CallonVector.clear(); // this will be rebuilt during the calls to UpdateTrain
9220 //elapsed time segment
9221 //Display->PerformanceLog(-1, "\n Train vector size: " + AnsiString(TrainVector.size()) + '\n');
9222 //Display->PerformanceLog(-1, "Start time list");
9223 //end elapsed time segment
9224  for(unsigned int x = 0; x < TrainVector.size(); x++)
9225  {
9226 //elapsed time segment
9227 //Start = double(GetTime()) * 86400; //secs
9228 //end elapsed time segment
9229  TrainVectorAt(33, x).UpdateTrain(0);
9230 //elapsed time segment
9231 //End = double(GetTime()) * 86400;
9232 //ElapsedTimeReport = TrainVectorAt(-1, x).TrainDataEntryPtr->ServiceReference + AnsiString(" ") + AnsiString(int((End - Start) * 1000)); //msecs
9233 //Display->PerformanceLog(-1, ElapsedTimeReport);
9234 //end elapsed time segment
9235 
9236 //end elapsed time investigations
9237 
9238  /* added HasTrainGone() condition below in v0.4c to prevent 2 trains both having TrainGone set in UpdateTrain
9239  at the same time. That caused the error Craig Weekes reported in November 2010 where 2 trains exited at the same time, and later the TrainVector
9240  iterates in reverse to erase the second train to have gone (when the first train to have gone comes before the second in TrainVector),
9241  but afterwards ReplotTrains iterates forwards and therefore replots the first train to have gone and therefore sets the TrainIDOnElement value
9242  to the exited train, with nothing to reset it. Hovering the mouse over that element with train information enabled causes an error because
9243  the track element thinks the train is still there, whereas it is missing from the TrainVector. BUT subsequently (in v2.11.1) changed RePlotTrains
9244  so it doesn't plot trains with TrainGone set, but left this is as does no harm
9245 
9246  Had another error notified by Kevin Smith on 02/01/22 where a train was manually removed in the same clock cycle as a train exited, and this caused
9247  the same error as above. Did a lot of experimenting but eventually cured it with two changes, first as above in RePlotTrains, and also below adding
9248  a break; command after one TrainHasGone() dealt with. There were introduced in v2.11.1 & seems ok now
9249 
9250  These changes should deal with any number of TrainGone flags set in the same clock cycle - from exiting, manual removal, or joins
9251  */
9252  if(TrainAdded || TrainVectorAt(35, x).HasTrainGone())
9253  {
9254  break; //only one exited train will be dealt with at a time (see below) so no point looking further
9255  }
9256  }
9257  // set warning flags
9258  CrashWarning = false;
9259  DerailWarning = false;
9260  SPADWarning = false;
9261  CallOnWarning = false;
9262  SignalStopWarning = false;
9263  BufferAttentionWarning = false;
9264  TrainFailedWarning = false;
9265  for(int x = TrainVector.size() - 1; x >= 0; x--) // reverse because of erase
9266  {
9267  TTrain &Train = TrainVectorAt(34, x);
9268  if(Train.Crashed)
9269  // can't use background colours for crashed & derailed because same colour
9270  {
9271  CrashWarning = true;
9272  }
9273  else if(Train.Derailed)
9274  // can't use background colours for crashed & derailed because same colour
9275  {
9276  DerailWarning = true;
9277  }
9278  else if(Train.BackgroundColour == clSPADBackground)
9279  // use colour as that changes as soon as passes signal
9280  {
9281  SPADWarning = true;
9282  }
9283  else if(Train.BackgroundColour == clTrainFailedBackground)
9284  {
9285  TrainFailedWarning = true;
9286  }
9287  else if(Train.BackgroundColour == clCallOnBackground)
9288  // use colour as also stopped at signal
9289  {
9290  CallOnWarning = true;
9291  }
9292  else if(Train.BackgroundColour == clSignalStopBackground)
9293  // use colour to distinguish from call-on
9294  {
9295  SignalStopWarning = true;
9296  }
9297  else if(Train.BackgroundColour == clBufferAttentionNeeded)
9298  // use colour to distinguish from ordinary buffer stop
9299  {
9300  BufferAttentionWarning = true;
9301  }
9302  if(Train.HasTrainGone())
9303  {
9304  AnsiString Loc = "";
9305  bool ElementFound = false;
9306  TTrackElement TE;
9307  if(Train.LagElement > -1)
9308  {
9309  TE = Track->TrackElementAt(531, Train.LagElement);
9310  ElementFound = true;
9311  }
9312  else if(Train.MidElement > -1)
9313  {
9314  TE = Track->TrackElementAt(779, Train.MidElement);
9315  ElementFound = true;
9316  }
9317  else if(Train.LeadElement > -1)
9318  {
9319  TE = Track->TrackElementAt(780, Train.LeadElement);
9320  ElementFound = true;
9321  }
9322  if(ElementFound)
9323  {
9324  if(TE.ActiveTrackElementName != "")
9325  {
9326  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9327  }
9328  else
9329  {
9330  Loc = "track element " + TE.ElementID;
9331  }
9332  }
9333  TActionVectorEntry *AVEntryPtr = Train.ActionVectorEntryPtr;
9334  if((Train.SignallerRemoved) || (Train.JoinedOtherTrainFlag))
9335  // need above first because may also have ActionVectorEntryPtr == "Fer"
9336  {
9337  Train.UnplotTrain(9);
9338  // added at v1.3.0 to reset signals after train removed from an autosigsroute
9340  {
9343  }
9344  // end of addition
9345  AllRoutes->RebuildRailwayFlag = true;
9346  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode, to replot LCs
9347  // correctly after a crash
9348  }
9349  else if(AVEntryPtr->Command == "Fer")
9350  {
9351  bool CorrectExit = false;
9352  if(!AVEntryPtr->ExitList.empty())
9353  {
9354  for(TNumListIterator ELIT = AVEntryPtr->ExitList.begin(); ELIT != AVEntryPtr->ExitList.end(); ELIT++)
9355  {
9356  if(*ELIT == Train.LagElement)
9357  {
9358  CorrectExit = true;
9359  }
9360  }
9361  }
9362  if(CorrectExit)
9363  {
9364  Train.LogAction(19, Train.HeadCode, "", Leave, Loc, AVEntryPtr->EventTime, AVEntryPtr->Warning);
9365  }
9366  else
9367  {
9368  LogActionError(38, Train.HeadCode, "", FailIncorrectExit, Loc);
9369  }
9370  }
9371  else
9372  {
9373  if(!AVEntryPtr->SignallerControl)
9374  {
9375  LogActionError(26, Train.HeadCode, "", FailUnexpectedExitRailway, Loc);
9376  Train.SendMissedActionLogs(2, -2, AVEntryPtr);
9377  // -2 is marker for send messages for all remaining actions except Fer if present
9378  }
9379  else
9380  {
9381  Train.LogAction(31, Train.HeadCode, "", SignallerLeave, Loc, TDateTime(0), false); // false for Warning
9382  }
9383  }
9384  Train.TrainDataEntryPtr->TrainOperatingDataVector.at(Train.RepeatNumber).RunningEntry = Exited;
9385  Train.DeleteTrain(1);
9386  TrainVector.erase(TrainVector.begin() + x);
9387  ReplotTrains(1, Display); //to reset ElementIDs for remaining trains when have removed a train
9388  //NB: won't plot any trains with TrainGone flag set (changed at v2.11.1)
9389  break; //added at v2.11.1 to ensure that only one train with TrainGone set is dealt with in one clock cycle
9390  }
9391  }
9392  }
9393  else
9394  {
9395  // reset all flags in case last train removed with flag set
9396  CrashWarning = false;
9397  DerailWarning = false;
9398  SPADWarning = false;
9399  CallOnWarning = false;
9400  SignalStopWarning = false;
9401  BufferAttentionWarning = false;
9402  TrainFailedWarning = false;
9403  }
9404  // update OpTimeToActMultimap
9406  {
9408  // clears entries then adds values for running trains then for continuation entries
9410  //added for multiplayer for running trains only
9411  }
9412  Utilities->Clock2Stopped = ClockState;
9413  Utilities->CallLogPop(723);
9414 }
9415 
9416 // ---------------------------------------------------------------------------
9418 {
9419  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",FinishedOperation");
9420  if(!TrainVector.empty())
9421  {
9422  for(int x = TrainVector.size() - 1; x >= 0; x--)
9423  {
9424  TrainVectorAt(50, x).DeleteTrain(2);
9425  }
9426  TrainVector.clear();
9427  }
9428  if(!TrainDataVector.empty())
9429  {
9430  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
9431  {
9432  TTrainDataEntry &TDEntry = TrainDataVector.at(x);
9433  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
9434  {
9435  TTrainOperatingData &TOD = TDEntry.TrainOperatingDataVector.at(y);
9436  TOD.RunningEntry = NotStarted;
9437  TOD.TrainID = -1;
9438  TOD.EventReported = NoEvent;
9439  }
9440  }
9441  }
9442  Display->GetOutputLog1()->Caption = "";
9443  Display->GetOutputLog2()->Caption = "";
9444  Display->GetOutputLog3()->Caption = "";
9445  Display->GetOutputLog4()->Caption = "";
9446  Display->GetOutputLog5()->Caption = "";
9447  Display->GetOutputLog6()->Caption = "";
9448  Display->GetOutputLog7()->Caption = "";
9449  Display->GetOutputLog8()->Caption = "";
9450  Display->GetOutputLog9()->Caption = "";
9451  Display->GetOutputLog10()->Caption = "";
9452  Utilities->CallLogPop(1352);
9453 }
9454 
9455 // ---------------------------------------------------------------------------
9456 
9458 {
9459  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ReplotTrains");
9460  if(!TrainVector.empty())
9461  {
9462  for(unsigned int x = 0; x < TrainVector.size(); x++)
9463  {
9464  if(!TrainVectorAt(84, x).HasTrainGone()) //added at v2.11.0 to prevent plotting a train pending removal & particularly to prevent TrainElementID's being reinstated
9465  { //see Kevin Smith error information for details
9466  TrainVectorAt(51, x).PlotTrain(4, Disp);
9467  }
9468  }
9469  }
9470  Utilities->CallLogPop(724);
9471 }
9472 
9473 // ---------------------------------------------------------------------------
9474 
9475 void TTrainController::WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
9476 {
9477  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WriteTrainsToImage");
9478  if(!TrainVector.empty())
9479  {
9480  for(unsigned int x = 0; x < TrainVector.size(); x++)
9481  {
9482  TrainVectorAt(61, x).WriteTrainToImage(0, Bitmap);
9483  }
9484  }
9485  Utilities->CallLogPop(1707);
9486 }
9487 
9488 // ---------------------------------------------------------------------------
9489 
9491 {
9492  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",UnplotTrains");
9493  if(!TrainVector.empty())
9494  {
9495  for(unsigned int x = 0; x < TrainVector.size(); x++)
9496  {
9497  TrainVectorAt(52, x).UnplotTrain(10);
9498  }
9499  }
9501  Utilities->CallLogPop(725);
9502 }
9503 
9504 // ---------------------------------------------------------------------------
9505 
9506 bool TTrainController::AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed,
9507  double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes,
9508  int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
9509 {
9510  LogEvent(AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) + "," + HeadCode + "," + AnsiString(StartSpeed) +
9511  "," + AnsiString(Mass) + "," + ModeStr);
9512  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",AddTrain," + AnsiString(RearPosition) + "," + AnsiString(FrontPosition) +
9513  "," + HeadCode + "," + AnsiString(StartSpeed) + "," + AnsiString(Mass) + "," + ModeStr); //at v2.11.1 dropped later headcode - was listed twice
9514 
9515  int RearExitPos = -1;
9516 
9517  for(int x = 0; x < 4; x++)
9518  {
9519  if(Track->TrackElementAt(519, RearPosition).Conn[x] == FrontPosition)
9520  {
9521  RearExitPos = x;
9522  }
9523  }
9524  if(RearExitPos == -1)
9525  {
9526  throw Exception("Error, RearExit == -1 in AddTrain");
9527  }
9528  bool ReportFlag = true;
9529 
9530  // used to stop repeated messages from CheckStartAllowable when split failed
9531  if(TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported != NoEvent)
9532  {
9533  ReportFlag = false;
9534  }
9535  if(!CheckStartAllowable(0, RearPosition, RearExitPos, HeadCode, ReportFlag, EventType))
9536  {
9537  // messages sent to performance log in CheckStartAllowable if ReportFlag true
9538  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = EventType;
9539  Utilities->CallLogPop(938);
9540  return(false);
9541  }
9542  TrainDataEntryPtr->TrainOperatingDataVector.at(RepeatNumber).EventReported = NoEvent;
9543  TTrainMode TrainMode = NoMode;
9544 
9545  if(ModeStr == "Timetable")
9546  {
9547  TrainMode = Timetable;
9548  }
9549  // all else gives 'None', 'Signaller' set within program
9550 
9551  if(MaxRunningSpeed < 10)
9552  {
9553  MaxRunningSpeed = 10; // added at v0.6 to avoid low max speeds
9554  }
9555  if(SignallerSpeed < 10)
9556  {
9557  SignallerSpeed = 10; // added at v0.6 to avoid low max speeds
9558  }
9559  TTrain *NewTrain = new TTrain(0, RearPosition, RearExitPos, HeadCode, StartSpeed, Mass, MaxRunningSpeed, MaxBrakeRate, PowerAtRail, TrainMode,
9560  TrainDataEntryPtr, RepeatNumber, IncrementalMinutes, IncrementalDigits, SignallerSpeed);
9561 
9562  LogEvent("AddTrainSupplemental: Service Ref = " + TrainDataEntryPtr->ServiceReference + ", TrainID = " + AnsiString(NewTrain->TrainID)); //new at v2.11.1 so can relate headcode to ID
9563 
9564  NewTrain->ActionVectorEntryPtr = &(TrainDataEntryPtr->ActionVector.at(0));
9565  // initialise here rather than in TTrain constructor as create trains
9566  // with Null TrainDataEntryPtr when loading session trains
9567  if(SignallerControl)
9568  {
9569  NewTrain->TimetableFinished = true;
9570  NewTrain->SignallerStoppingFlag = false;
9571  NewTrain->TrainMode = Signaller;
9572  if(NewTrain->MaxRunningSpeed > NewTrain->SignallerMaxSpeed)
9573  {
9574  NewTrain->MaxRunningSpeed = NewTrain->SignallerMaxSpeed;
9575  }
9577  }
9578  // deal with starting conditions:-
9579  // unlocated Snt: just report entry & advance pointer
9580  // located Snt or Sfs: set station conditions as would if had reached stop point in Update(), & advance the ActionVectorEntryPtr
9581  // Sns doesn't need a new train
9582  if(NewTrain->ActionVectorEntryPtr->LocationName != "")
9583  // covers all above located starts
9584  // if location of Snt was a station (that is set as LocationName, i.e. not just any station) that isn't next departure station then
9585  // wouldn't have accepted the timetable
9586  {
9587  // first check if LeadElement (can't access LeadElement directly yet as not set, use FrontPosition instead) is buffers, note that
9588  // StoppedAtBuffers is set in UpdateTrain()
9589  if(Track->TrackElementAt(520, FrontPosition).TrackType == Buffers)
9590  // buffer end must be ahead of train or would have failed start position check
9591  {
9592  NewTrain->StoppedAtLocation = true;
9593  NewTrain->PlotStartPosition(0);
9595  NewTrain->LogAction(20, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9596  NewTrain->ActionVectorEntryPtr->Warning);
9597  if(!SignallerControl) // don't advance if SignalControlEntry
9598  {
9599  NewTrain->ActionVectorEntryPtr++;
9600  // should be a command, could be a location departure but if so can't depart so set 'Hold' anyway
9601  }
9602  NewTrain->LastActionTime = TTClockTime;
9603  }
9604  // else a through station stop
9605  else
9606  {
9607  NewTrain->StoppedAtLocation = true;
9608  NewTrain->PlotStartPosition(10);
9610  NewTrain->LogAction(21, NewTrain->HeadCode, "", Create, NewTrain->ActionVectorEntryPtr->LocationName, NewTrain->ActionVectorEntryPtr->EventTime,
9611  NewTrain->ActionVectorEntryPtr->Warning);
9612  if(!SignallerControl) // don't advance if SignalControlEntry
9613  {
9614  NewTrain->ActionVectorEntryPtr++;
9615  }
9616  NewTrain->LastActionTime = TTClockTime;
9617  }
9618  }
9619  else // unlocated entry (i.e. not a stop entry, but could still be at a named location)
9620  {
9621  NewTrain->PlotStartPosition(11);
9622  TTrackElement TE = Track->TrackElementAt(530, NewTrain->RearStartElement);
9623  AnsiString Loc = "";
9624  if(TE.ActiveTrackElementName != "")
9625  {
9626  Loc = TE.ActiveTrackElementName + ", track element " + TE.ElementID;
9627  }
9628  else
9629  {
9630  Loc = "track element " + TE.ElementID;
9631  }
9632  if(TE.TrackType == Continuation)
9633  {
9634  NewTrain->LogAction(22, NewTrain->HeadCode, "", Enter, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9635  }
9636  else
9637  {
9638  NewTrain->LogAction(23, NewTrain->HeadCode, "", Create, Loc, NewTrain->ActionVectorEntryPtr->EventTime, NewTrain->ActionVectorEntryPtr->Warning);
9639  }
9640  if(!SignallerControl) // don't advance if SignalControlEntry
9641  {
9642  NewTrain->ActionVectorEntryPtr++;
9643  }
9644  NewTrain->LastActionTime = TTClockTime;
9645  // no need to set LastActionTime for an unlocated entry
9646  }
9647  // cancel a wrong-direction route if either element of train starts on one
9648  if(NewTrain->LeadElement > -1)
9649  {
9650  NewTrain->CheckAndCancelRouteForWrongEndEntry(3, NewTrain->LeadElement, NewTrain->LeadEntryPos);
9651  }
9652  if(NewTrain->MidElement > -1)
9653  {
9654  NewTrain->CheckAndCancelRouteForWrongEndEntry(4, NewTrain->MidElement, NewTrain->MidEntryPos);
9655  }
9656  // set signals for a right-direction autosigs route for either element of train on one
9657  // erase elements back to start for a non-autosigs route & check if an autosigs route immediately behind it, and if so set its signals
9658  // note that all but autosigs routes become part of a single route, so there can only be an autosigs route behind the non-autosigs route
9659  int RouteNumber = -1;
9660  bool SignalsSet = false;
9661 
9662  if(NewTrain->LeadElement > -1)
9663  {
9664  if(AllRoutes->GetRouteTypeAndNumber(13, NewTrain->LeadElement, NewTrain->LeadEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9665  {
9666  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9667  int RouteStartPosition;
9668  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9669  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(21, Track->TrackElementAt(955, FrontPosition).HLoc,
9670  Track->TrackElementAt(956, FrontPosition).VLoc, SecondPair);
9671  if(FirstPair.first == RouteNumber)
9672  {
9673  RouteStartPosition = FirstPair.second;
9674  }
9675  else if(SecondPair.first == RouteNumber)
9676  {
9677  RouteStartPosition = SecondPair.second;
9678  }
9679  else
9680  {
9681  throw Exception("Error, RouteNumber not found in Route2MultiMap in 1st of 2 calls to SetAllRearwardsSignals in AddTrain");
9682  }
9683  AllRoutes->SetAllRearwardsSignals(10, 0, RouteNumber, RouteStartPosition);
9684  SignalsSet = true;
9685  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9686  }
9687  else if(RouteNumber > -1) // non-autosigsroute
9688  {
9689  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(181, RouteNumber).GetFixedPrefDirElementAt(194, 0);
9690  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9691  int FirstELinkPos = TempPDE.GetELinkPos();
9692  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->LeadElement))
9693  {
9694  AllRoutes->RemoveRouteElement(16, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9695  TempPDE = AllRoutes->GetFixedRouteAt(182, RouteNumber).GetFixedPrefDirElementAt(195, 0);
9696  }
9697  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->LeadElement))
9698  {
9699  AllRoutes->RemoveRouteElement(17, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9700  // remove the last element under LeadElement
9701  }
9702  AllRoutes->RebuildRailwayFlag = true;
9703  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9704  // now deal with a rear linked autosigs route
9705  if(Track->TrackElementAt(820, FirstTVPos).Conn[FirstELinkPos] > -1)
9706  {
9707  int LinkedRouteNumber = -1;
9708  if(AllRoutes->GetRouteTypeAndNumber(17, Track->TrackElementAt(821, FirstTVPos).Conn[FirstELinkPos],
9709  Track->TrackElementAt(822, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9710  {
9711  AllRoutes->GetFixedRouteAt(169, LinkedRouteNumber).SetRouteSignals(0);
9712  // this is ok as here we are setting signals from the start of the route
9713  }
9714  }
9715  SignalsSet = true;
9716  }
9717  }
9718  if(NewTrain->MidElement > -1)
9719  // if entering at a continuation MidElement == -1
9720  {
9721  // this is included in case a train starts with LeadElement on no route and MidElement on a route
9722  if(!SignalsSet)
9723  {
9724  RouteNumber = -1;
9725  if(AllRoutes->GetRouteTypeAndNumber(14, NewTrain->MidElement, NewTrain->MidEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
9726  {
9727  // below added in place of SetRouteSignals in v2.4.0 as don't want to set signals from start of route for a new train addition
9728  int RouteStartPosition;
9729  TAllRoutes::TRouteElementPair FirstPair, SecondPair;
9730  FirstPair = AllRoutes->GetRouteElementDataFromRoute2MultiMap(22, Track->TrackElementAt(957, RearPosition).HLoc,
9731  Track->TrackElementAt(958, RearPosition).VLoc, SecondPair);
9732  if(FirstPair.first == RouteNumber)
9733  {
9734  RouteStartPosition = FirstPair.second;
9735  }
9736  else if(SecondPair.first == RouteNumber)
9737  {
9738  RouteStartPosition = SecondPair.second;
9739  }
9740  else
9741  {
9742  throw Exception("Error, RouteNumber not found in Route2MultiMap in 2nd of 2 calls to SetAllRearwardsSignals in AddTrain");
9743  }
9744  AllRoutes->SetAllRearwardsSignals(11, 0, RouteNumber, RouteStartPosition);
9745  SignalsSet = true;
9746  // AllRoutes->GetFixedRouteAt(, RouteNumber).SetRouteSignals(); above substituted in v2.4.0
9747  }
9748  else if(RouteNumber > -1) // non-autosigsroute
9749  {
9750  TPrefDirElement TempPDE = AllRoutes->GetFixedRouteAt(184, RouteNumber).GetFixedPrefDirElementAt(196, 0);
9751  int FirstTVPos = TempPDE.GetTrackVectorPosition();
9752  int FirstELinkPos = TempPDE.GetELinkPos();
9753  while(TempPDE.GetTrackVectorPosition() != (unsigned int)(NewTrain->MidElement))
9754  {
9755  AllRoutes->RemoveRouteElement(18, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9756  TempPDE = AllRoutes->GetFixedRouteAt(185, RouteNumber).GetFixedPrefDirElementAt(197, 0);
9757  }
9758  if(TempPDE.GetTrackVectorPosition() == (unsigned int)(NewTrain->MidElement))
9759  {
9760  AllRoutes->RemoveRouteElement(19, TempPDE.HLoc, TempPDE.VLoc, TempPDE.GetELink());
9761  // remove the last element under LeadElement
9762  }
9763  AllRoutes->RebuildRailwayFlag = true;
9764  // to force ClearandRebuildRailway at next clock tick if not in zoom-out mode
9765  // now deal with a rear linked autosigs route
9766  if(Track->TrackElementAt(823, FirstTVPos).Conn[FirstELinkPos] > -1)
9767  {
9768  int LinkedRouteNumber = -1;
9769  if(AllRoutes->GetRouteTypeAndNumber(19, Track->TrackElementAt(824, FirstTVPos).Conn[FirstELinkPos],
9770  Track->TrackElementAt(825, FirstTVPos).ConnLinkPos[FirstELinkPos], LinkedRouteNumber) == TAllRoutes::AutoSigsRoute)
9771  {
9772  AllRoutes->GetFixedRouteAt(170, LinkedRouteNumber).SetRouteSignals(1);
9773  // this is ok as now we are setting signals from the start of the route
9774  }
9775  }
9776  }
9777  }
9778  }
9779  TrainVector.push_back(*NewTrain);
9780  Utilities->CallLogPop(731);
9781  return(true);
9782 }
9783 
9784 // ---------------------------------------------------------------------------
9785 
9786 int TTrainController::EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
9787 {
9788  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",EntryPos," + AnsiString(TrainIDIn) + "," +
9789  AnsiString(TrackVectorNumber));
9790  int VecPos = -1;
9791 
9792  for(unsigned int x = 0; x < TrainVector.size(); x++)
9793  {
9794  if(TrainVectorAt(1, x).TrainID == TrainIDIn)
9795  {
9796  VecPos = x;
9797  }
9798  }
9799  if(VecPos == -1)
9800  {
9801  throw Exception("Error, VecPos not set in EntryPos");
9802  }
9803  if(TrainVectorAt(2, VecPos).LeadElement == TrackVectorNumber)
9804  {
9805  Utilities->CallLogPop(734);
9806  return(TrainVectorAt(3, VecPos).LeadEntryPos);
9807  }
9808  else if(TrainVectorAt(4, VecPos).MidElement == TrackVectorNumber)
9809  {
9810  Utilities->CallLogPop(735);
9811  return(TrainVectorAt(5, VecPos).MidEntryPos);
9812  }
9813  else if(TrainVectorAt(6, VecPos).LagElement == TrackVectorNumber)
9814  {
9815  Utilities->CallLogPop(736);
9816  return(TrainVectorAt(7, VecPos).LagEntryPos);
9817  }
9818  Utilities->CallLogPop(737);
9819  return(-1);
9820 }
9821 
9822 // ---------------------------------------------------------------------------
9823 
9825 {
9826  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAtIdent," + AnsiString(TrainID));
9827  for(unsigned int x = 0; x < TrainVector.size(); x++)
9828  {
9829  if(TrainVectorAt(53, x).TrainID == TrainID)
9830  {
9831  Utilities->CallLogPop(738);
9832  return(TrainVectorAt(54, x));
9833  }
9834  }
9835  throw Exception("Error - No Train identified in TrainVectorAtIdent with ID = " + AnsiString(TrainID));
9836 }
9837 
9838 // ---------------------------------------------------------------------------
9839 
9840 bool TTrainController::TrainExistsAtIdent(int Caller, int TrainID)
9841 // return true if find the train (added at v2.4.0 as can select a removed train
9842 // in OAListBox before it updates - reported by LiWinDom in error report 23/04/20)
9843 {
9844  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainExistsAtIdent," + AnsiString(TrainID));
9845  for(unsigned int x = 0; x < TrainVector.size(); x++)
9846  {
9847  if(TrainVectorAt(69, x).TrainID == TrainID)
9848  {
9849  Utilities->CallLogPop(2152);
9850  return(true);
9851  }
9852  }
9853  Utilities->CallLogPop(2153);
9854  return(false);
9855 }
9856 
9857 // ---------------------------------------------------------------------------
9858 
9859 TDateTime TTrainController::GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
9860 {
9861  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetControllerTrainTime," + AnsiString(RepeatNumber) + "," +
9862  Utilities->Format96HHMMSS(Time));
9863  TDateTime RepeatTime = TrainController->GetRepeatTime(47, Time, RepeatNumber, IncrementalMinutes);
9864 
9865  Utilities->CallLogPop(2061);
9866  return(RepeatTime);
9867 }
9868 
9869 // ---------------------------------------------------------------------------
9870 
9871 AnsiString TTrainController::ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepNum, int IncMins, int IncDig)
9872 // Enter with Ptr pointing to first action to be listed (i.e. next action)
9873 {
9874  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ContinuationEntryFloatingTTString" + "," + TTDEPtr->HeadCode);
9875  AnsiString RetStr = "", PartStr = "";
9876  int Count = 0;
9877  TActionVectorIterator Ptr = TTDEPtr->ActionVector.begin();
9878 
9879  Ptr--; // because incremented at start of loop
9880  do
9881  {
9882  Ptr++;
9883  if((Ptr->Command != "") && (Ptr->Command[1] == 'S'))
9884  {
9885  continue; // move past the starting entry
9886  }
9887  if((Ptr->FormatType == Repeat) || Ptr >= TTDEPtr->ActionVector.end())
9888  {
9889  break;
9890  }
9891  if(Ptr->SignallerControl)
9892  {
9893  RetStr = "Train under signaller control";
9894  break;
9895  }
9896  if(Ptr->FormatType == TimeTimeLoc)
9897  {
9898  if(Ptr->ArrivalTime == Ptr->DepartureTime)
9899  {
9900  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(0, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive & depart from " + Ptr->LocationName;
9901  }
9902  else
9903  {
9904  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(1, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName + '\n' +
9905  Utilities->Format96HHMM(GetControllerTrainTime(2, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9906  Count++; // because there are 2 entries
9907  }
9908  }
9909  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime != TDateTime(-1)))
9910  {
9911  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(3, Ptr->ArrivalTime, RepNum, IncMins)) + ": Arrive at " + Ptr->LocationName;
9912  }
9913  else if((Ptr->FormatType == TimeLoc) && (Ptr->ArrivalTime == TDateTime(-1)))
9914  {
9915  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(4, Ptr->DepartureTime, RepNum, IncMins)) + ": Depart from " + Ptr->LocationName;
9916  }
9917  else if(Ptr->FormatType == PassTime) // new
9918  {
9919  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(5, Ptr->EventTime, RepNum, IncMins)) + ": Pass " + Ptr->LocationName;
9920  }
9921  else if(Ptr->Command == "Fns")
9922  {
9923  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(6, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9924  TrainController->GetRepeatHeadCode(46, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9925  PartStr = ControllerGetNewServiceDepartureInfo(0, Ptr, RepNum, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to PartStr
9926  }
9927  else if(Ptr->Command == "F-nshs")
9928  {
9929  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(7, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9930  Ptr->NonRepeatingShuttleLinkHeadCode + " at " + Ptr->LocationName;
9931  PartStr = ControllerGetNewServiceDepartureInfo(1, Ptr, 0, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9932  //note that use LinkedTrainEntryPtr and not NonRepeatingShuttleLinkEntryPtr because the forward link from the feeder is LinkedTrainEntryPtr.
9933  //NonRepeatingShuttleLinkEntryPtr is in the shuttle's ActionVector to point back to the feeder.
9934  //NonRepeatingShuttleLinkEntryPtr is used below from the last shuttle as the forward link to the finishing service
9935  }
9936 //Since this is a new continuation entry service it can't be Fns-sh or Frh-sh but leave these in for consistency with TTrain::FloatingTimetableString
9937  else if((Ptr->Command == "Fns-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9938  {
9939  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(8, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9940  TrainController->GetRepeatHeadCode(47, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9941  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9942  PartStr = ControllerGetNewServiceDepartureInfo(2, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9943  }
9944  else if((Ptr->Command == "Fns-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9945  {
9946  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(9, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9947  Ptr->NonRepeatingShuttleLinkHeadCode, +" at " + Ptr->LocationName;
9948  PartStr = ControllerGetNewServiceDepartureInfo(3, Ptr, 0, TTDEPtr, Ptr->NonRepeatingShuttleLinkEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9949  }
9950  else if((Ptr->Command == "Frh-sh") && (RepNum < (TTDEPtr->NumberOfTrains - 1))) // not the last repeat number
9951  {
9952  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(10, Ptr->EventTime, RepNum, IncMins)) + ": Form new service " +
9953  TrainController->GetRepeatHeadCode(48, Ptr->OtherHeadCode, RepNum + 1, IncDig) + " at " + Ptr->LocationName;
9954  // use RepNum+1 because it's the repeat number of the NEXT shuttle service that is relevant
9955  PartStr = ControllerGetNewServiceDepartureInfo(4, Ptr, RepNum + 1, TTDEPtr, Ptr->LinkedTrainEntryPtr, IncMins, PartStr); //if there is a next service this adds the new service departure time to RetStr
9956  }
9957  else if((Ptr->Command == "Frh-sh") && (RepNum >= (TTDEPtr->NumberOfTrains - 1))) // last repeat number
9958  {
9959  PartStr = "Terminate at " + Ptr->LocationName;
9960  }
9961  else if(Ptr->Command == "Frh")
9962  {
9963  PartStr = "Terminate at " + Ptr->LocationName;
9964  }
9965  else if(Ptr->Command == "Fer")
9966  {
9967  AnsiString AllowedExits;
9968  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(11, Ptr->EventTime, RepNum, IncMins)) + ": Exit railway" +
9969  TrainController->GetExitLocationAndAt(3, Ptr->ExitList, AllowedExits) + AllowedExits;
9970  }
9971  else if(Ptr->Command == "Fjo")
9972  {
9973  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(12, Ptr->EventTime, RepNum, IncMins)) + ": Join " + TrainController->GetRepeatHeadCode(49,
9974  Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9975  }
9976  else if(Ptr->Command == "jbo")
9977  {
9978  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(13, Ptr->EventTime, RepNum, IncMins)) + ": Joined by " + TrainController->GetRepeatHeadCode
9979  (50, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9980  }
9981  else if(Ptr->Command == "fsp")
9982  {
9983  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(14, Ptr->EventTime, RepNum, IncMins)) + ": Front split to " +
9984  TrainController->GetRepeatHeadCode(51, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9985  }
9986  else if(Ptr->Command == "rsp")
9987  {
9988  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(15, Ptr->EventTime, RepNum, IncMins)) + ": Rear split to " +
9989  TrainController->GetRepeatHeadCode(52, Ptr->OtherHeadCode, RepNum, IncDig) + " at " + Ptr->LocationName;
9990  }
9991  else if(Ptr->Command == "cdt")
9992  {
9993  PartStr = Utilities->Format96HHMM(GetControllerTrainTime(16, Ptr->EventTime, RepNum, IncMins)) + ": Change direction at " + Ptr->LocationName;
9994  }
9995  if(RetStr != "")
9996  {
9997  RetStr = RetStr + '\n' + PartStr;
9998  }
9999  else
10000  {
10001  RetStr = PartStr;
10002  }
10003  Count++;
10004  }
10005  while(Ptr < TTDEPtr->ActionVector.end() && (Count < 33) && ((Ptr->Command == "") || ((Ptr->Command != "") && (Ptr->Command[1] != 'F'))));
10006  // limit of 33 allows a max of 34 entries (may have gone from 32 to 34 because of a TimeTimeLoc), which with track and train status gives
10007  // a max of 48 lines, at 13 pixels each, = 624 pixels & screen height has 641 so will fit comfortably. Also 34 timetable entries is as far
10008  // forward as anyone should wish to see without looking at the full timetable
10009  Utilities->CallLogPop(2072);
10010  return(RetStr);
10011 }
10012 
10013 // ---------------------------------------------------------------------------
10014 
10015 AnsiString TTrainController::ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
10016 {
10017  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString(Ptr - &TDEPtr->ActionVector.front()) + ","
10018  + AnsiString(RptNum) + ",ControllerGetNewServiceDepartureInfo," + TDEPtr->HeadCode);
10019  AnsiString DepTime = "", EventTime = "";
10020  bool CDTFlag = false; //reports if train changes direction before departs
10021  TActionVector NewServiceAV = LinkedTrainDataPtr->ActionVector;
10022  AnsiString CurrentLocation = NewServiceAV.at(0).LocationName; //added at v2.12.0 to show departure direction
10023  AnsiString TowardsLocation = ""; //added at v2.12.0 to show departure direction
10024  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++) //added at v2.12.0 to obtain departure direction
10025  {
10026  if((AVI->LocationName != CurrentLocation) && (AVI->LocationName != "") && (TowardsLocation == ""))
10027  {
10028  TowardsLocation = AVI->LocationName;
10029  }
10030  else if((AVI->Command == "Fer") && (TowardsLocation == "") && !AVI->ExitList.empty())
10031  {
10032  TTrackElement TE = Track->TrackElementAt(7777, (AVI->ExitList.front()));
10033  if(TE.ActiveTrackElementName != "")
10034  {
10035  TowardsLocation = TE.ActiveTrackElementName;
10036  }
10037  else
10038  {
10039  TowardsLocation = AnsiString("track element ID ") + TE.ElementID;
10040  }
10041  }
10042  }
10043  for(TActionVectorIterator AVI = NewServiceAV.begin(); AVI < NewServiceAV.end(); AVI++)
10044  {
10045  if(AVI->Command == "cdt")
10046  {
10047  CDTFlag = !CDTFlag; //toggles flag - allows for there being more than one cdt before departure
10048  continue;
10049  }
10050  if((AVI->Command == "fsp") || (AVI->Command == "rsp"))
10051  {
10052  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(21, AVI->EventTime, RptNum, IncrementalMinutes));
10053  RetStr += "\nNew service splits at " + EventTime;
10054  Utilities->CallLogPop(2237);
10055  return(RetStr);
10056  }
10057  if(AVI->Command == "jbo")
10058  {
10059  EventTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(22, AVI->EventTime, RptNum, IncrementalMinutes));
10060  RetStr += "\nNew service joined by " + AVI->OtherHeadCode + " at " + EventTime;
10061  Utilities->CallLogPop(2238);
10062  return(RetStr);
10063  }
10064  if((AVI->FormatType == TimeLoc) && (AVI->DepartureTime > TDateTime(-1))) //departure time set
10065  {
10066  DepTime = Utilities->Format96HHMM(TrainController->GetControllerTrainTime(23, AVI->DepartureTime, RptNum, IncrementalMinutes));
10067  if(CDTFlag)
10068  {
10069  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10070  {
10071  RetStr += "\nNew service changes direction then departs towards " + TowardsLocation + " at " + DepTime;
10072  }
10073  else
10074  {
10075  RetStr += "\nNew service changes direction then departs at " + DepTime;
10076  }
10077  }
10078  else
10079  {
10080  if(TowardsLocation != "") //added at v2.12.0 to show departure direction
10081  {
10082  RetStr += "\nNew service departs towards " + TowardsLocation + " at " + DepTime;
10083  }
10084  else
10085  {
10086  RetStr += "\nNew service departs at " + DepTime;
10087  }
10088  }
10089  Utilities->CallLogPop(2239);
10090  return(RetStr);
10091  }
10092  }
10093  Utilities->CallLogPop(2223);
10094  return(RetStr);
10095 }
10096 
10097 // ---------------------------------------------------------------------------
10098 // $$$$$$$$$$$$$$$$$$$$$$ Start of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$$
10099 /*
10100  Note: The terms 'action' and 'entry' have been used freely for individual code lines within services in comments & in variable names, but
10101  for messages and in the manual and help files the term Entry is reserved for a complete service or train (i.e. an entry in the timetable),
10102  and 'event' is reserved for and individual code line within a service. Repeats use the term 'item' if they use any at all.
10103 
10104  In references to 'HeadCode' can have an optional prefix - up to 4 additional characters that can be anything, so long as last 4 digits
10105  represent the headcode. This allows links to be uniquely identified regardless of the headcode - so can have same headcodes as often as
10106  user wishes
10107 
10108  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10109  descriptive text or anything user wishes
10110  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10111  be ignored) is taken as the timetable start time.
10112  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10113  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10114  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10115  within the timetable if required.
10116  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10117  services)
10118  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10119  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10120 
10121  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10122  text timetable file easier
10123 
10124  form:-
10125  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10126  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10127  then multiple entries, separated by commas, of the form:-
10128 
10129  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10130  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
10131  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
10132 
10133  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
10134  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
10135  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
10136 
10137  HH:MM;Command (cdt) }TimeCmd }
10138  HH:MM;Location (arr & dep) }TimeLoc }
10139  HH:MM;HH:MM;Location }TimeTimeLoc }
10140  HH:MM;pas;Location }PassTime }
10141  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
10142  HH:MM;Fer;set of allowable IDs }ExitRailway }
10143  Command (Frh only) }FinRemHere }
10144 
10145  R;mm;dd;nn. Repeat Repeat entry
10146 
10147  Formats:
10148 
10149  Command only: Frh
10150  Time;Command: cdt
10151  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
10152  Time;Command;2 Element IDs: Snt
10153  Time;Comand;n Element IDs: Fer
10154  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
10155  Time;Command;2 Element IDs;Headcode Snt-sh
10156  Time;Command;Location pas
10157  Time;Location Arr Dep
10158  Time;Time;Location Arr & dep together
10159 
10160  9 Single entries: Snt (located or unlocated); pas; cdt; TimeLoc arr & dep; TimeTimeLoc; Fer; Frh
10161 
10162  9 1x Linked entries: Non-shuttle: fsp or rsp -> Sfs; Fns -> Sns; Fjo -> jbo; times must match, headcodes must match
10163  Shuttle: F-nshs -> Sns-sh: times match, F-nshs HeadCode matches Sns-sh 2nd Headcode;
10164  Fns-sh -> Sns-fsh: Fns-sh time + all repeats = Sns-fsh time, Fns-sh 2nd headcode matches Sns-fsh Headcode
10165 
10166  4 2x Linked entries, all shuttles:
10167 
10168  Frh-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Frh-sh Headcode = Snt-sh Headcode;
10169  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Frh-sh Headcode = Sns-sh 1st Headcode;
10170  -> Remain Here (at finish location after all repeats)
10171  Fns-sh -> Snt-sh: Frh-sh time = Snt-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Snt-sh Headcode
10172  -> Sns-sh: Frh-sh time = Sns-sh time + 1 repeat while repeating, Fns-sh 1st Headcode = Sns-sh 1st Headcode
10173 
10174  Allowable successors:-
10175 
10176  Successor state Type
10177 
10178  Snt located AtLoc ) Snt AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
10179  Snt Unlocated Moving ) Snt Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
10180  Sfs AtLoc )
10181  Sns AtLoc ) Start
10182  Sns-fsh AtLoc )
10183  Snt-sh AtLoc )
10184  Sns-sh AtLoc )
10185 
10186  pas Moving )
10187  jbo AtLoc )
10188  fsp AtLoc )
10189  rsp AtLoc ) Intermediate
10190  cdt AtLoc )
10191  TimeLoc arr Moving (bef), AtLoc (aft) )
10192  TimeLoc dep AtLoc (bef), Moving (aft) )
10193  TimeTimeLoc Moving )
10194 
10195  Fns Repeat/Nothing)
10196  Fjo Repeat/Nothing)
10197  Frh Repeat/Nothing)
10198  Fer Repeat/Nothing) Finish
10199  Frh-sh Repeat )
10200  Fns-sh Repeat )
10201  F-nshs Nothing )
10202 
10203  Descriptions:
10204  Snt New train
10205  Sfs New service from split
10206  Sns New service from another service
10207  Sns-fsh New non-repeating service from a shuttle service
10208  Snt-sh New shuttle train at a timetabled stop
10209  Sns-sh New shuttle service from a feeder service
10210 
10211  pas Pass
10212  jbo Be joined by another train
10213  fsp Front split
10214  rsp Rear split
10215  cdt Change direction of train
10216  TimeLoc arr Arrival
10217  TimeLoc dep Departure
10218  TimeTimeLoc Arrival and departure
10219 
10220  Fns Finish & form a new service
10221  Fjo Finish & join another train
10222  Frh Finish & remain here
10223  Fer Finish & exit railway
10224  Frh-sh Finish & repeat shuttle, finally remain here
10225  Fns-sh Finish & repeat shuttle, finally form a non-repeating service
10226  F-nshs Finish & form a shuttle feeder service
10227 */
10228 
10229 bool TTrainController::TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway) // true for success
10230 {
10231  // Error messages mainly given in called functions, five are given here - empty file; inability to find a start time; timetable containing
10232  // a line that is too long; timetable containing too few lines; and timetable failed to open.
10233  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TimetableIntegrityCheck," + AnsiString(FileName));
10234  // new for v0.2b
10235  // compile ActiveTrackElementNameMap
10236  TTrack::TActiveTrackElementNameMapEntry ActiveTrackElementNameMapEntry;
10237 
10239  for(unsigned int x = 0; x < Track->TrackVector.size(); x++)
10240  {
10241  // if((Track->TrackElementAt(, x).ActiveTrackElementName != "") && (Track->TrackElementAt(, x).TrackType != Continuation))
10243  == Track->ContinuationNameMap.end())
10244  {
10245  // exclude any name that appears in a continuation, error message given in tt validation if try to include such a name in a tt
10246  ActiveTrackElementNameMapEntry.first = Track->TrackElementAt(1035, x).ActiveTrackElementName;
10247  ActiveTrackElementNameMapEntry.second = 0; // this is a dummy value
10248  Track->ActiveTrackElementNameMap.insert(ActiveTrackElementNameMapEntry);
10249  }
10250  }
10252  // end of new section
10253  std::ifstream TTBLFile(FileName, std::ios_base::binary);
10254 
10255  // binary mode so the "\r\n" pairs stay as they are rather than being entered as '\n'
10256  if(TTBLFile.is_open())
10257  {
10258  char *TrainTimetableString = new char[10000];
10259  // enough for over 200 stations, should be adequate!
10260  bool EndOfFile = false;
10261  int Count = 0;
10262  // counts 'relevant' lines, i.e ignores any before the start time on its own line
10263  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10264  // delimiter is '\0' as it's an AnsiString
10265  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10266  // file empty - stores a null in 1st position if doesn't load any characters
10267  {
10268  // may still have eof even if read a line (no CRLF at end), and
10269  // if so need to process it
10270  TimetableMessage(GiveMessages, "Timetable invalid - file empty");
10271  TTBLFile.close();
10272  delete[] TrainTimetableString;
10273  Utilities->CallLogPop(1611);
10274  return(false);
10275  }
10276  AnsiString OneLine(TrainTimetableString);
10277  bool FinalCallFalse = false;
10278  while((Count == 0) && !ProcessOneTimetableLine(5, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10279  // get rid of lines before the start time
10280  {
10281  // ProcessOneTimetableLine returns true for a valid start time, an EndOfFile &/or a blank entry
10282  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10283  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10284  // stores a null in 1st position if doesn't load any characters
10285  {
10286  // may still have eof even if read a line (no CRLF at end), and
10287  // if so need to process it
10288  TimetableMessage(GiveMessages, "Timetable invalid - unable to find a valid start time on its own line");
10289  TTBLFile.close();
10290  delete[] TrainTimetableString;
10291  Utilities->CallLogPop(772);
10292  return(false);
10293  }
10294  OneLine = AnsiString(TrainTimetableString);
10295  }
10296  // here when have accepted the start time
10297  Count++; // increment past the start time
10298  while(!EndOfFile)
10299  {
10300  TTBLFile.getline(TrainTimetableString, 10000, '\0');
10301  // get next line after start time
10302  if(TTBLFile.eof() && (TrainTimetableString[0] == '\0'))
10303  // stores a null in 1st position if doesn't load any characters
10304  {
10305  // may still have eof even if read a line (no CRLF at end), and
10306  // if so need to process it
10307  EndOfFile = true;
10308  OneLine = "";
10309  }
10310  else
10311  {
10312  OneLine = AnsiString(TrainTimetableString);
10313  }
10314  if(OneLine.Length() > 9999)
10315  {
10316  TimetableMessage(GiveMessages, "Timetable contains a line that is too long - 10,000 or more characters!");
10317  TTBLFile.close();
10318  delete[] TrainTimetableString;
10319  Utilities->CallLogPop(789);
10320  return(false);
10321  }
10322  bool FinalCallFalse = false;
10323  if(!ProcessOneTimetableLine(6, Count, OneLine, EndOfFile, FinalCallFalse, GiveMessages, CheckLocationsExistInRailway))
10324  // false for FinalCall - just checking at this stage
10325  {
10326  TTBLFile.close();
10327  delete[] TrainTimetableString;
10328  Utilities->CallLogPop(770);
10329  return(false);
10330  }
10331  if(EndOfFile && (Count < 2))
10332  // Timetable must contain at least two relevant lines, one for start time and at least one train
10333  {
10334  TimetableMessage(GiveMessages, "Timetable has too few or no relevant entries - must have a start time on its own line and at least one train");
10335  TTBLFile.close();
10336  delete[] TrainTimetableString;
10337  Utilities->CallLogPop(771);
10338  return(false);
10339  }
10340  Count++;
10341  }
10342  delete[] TrainTimetableString;
10343  TTBLFile.close();
10344  } // if(TTBLFile.is_open())
10345  else
10346  {
10347  TimetableMessage(GiveMessages, "Failed to open timetable file, make sure it's spelled correctly, it exists and isn't open in another application");
10348  Utilities->CallLogPop(2154);
10349  return(false);
10350  }
10351  Utilities->CallLogPop(753);
10352  return(true);
10353 }
10354 
10355 // ---------------------------------------------------------------------------
10356 
10357 bool TTrainController::ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages,
10358  bool CheckLocationsExistInRailway) // return true for success
10359 
10360 /* Format:
10361  Prior to start time, anything except a line beginning with a time [...leading spaces...] HH:MM is ignored - can be
10362  descriptive text or anything user wishes
10363  A time on its own line [HH:MM], with or without leading spaces, but with anything following it before the CR (which will
10364  be ignored) is taken as the timetable start time.
10365  Thereafter there must be text on every line in the timetable, as the first blank line (or end of file) will be taken as the end of the
10366  timetable. Text can follow the 'end of timetable' blank line if the user wishes.
10367  A line within the timetable beginning with '*', with or without leading spaces, is ignored. Such lines can add text
10368  within the timetable if required.
10369  Timetable entries consist of one line per headcode (i.e. per service, not necessarily per train, as one train can run several different
10370  services)
10371  Each line starts with HeadCode & full train information for a new train (Snt or Snt-sh), or, for a continuing service
10372  (Sfs, Sns, Sns-sh or Sns-fsh), can have (a) Headcode only or (b) HeadCode + Description, nothing else
10373 
10374  All leading & trailing spaces before & after a line or any entry in a line are stripped off - these can be included to make reading a
10375  text timetable file easier
10376 
10377  form:-
10378  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
10379  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
10380  then multiple entries, separated by commas, of the form:-
10381 
10382  Format FormatType
10383  [W]HH:MM;Command (cdt) }TimeCmd }
10384  [W]HH:MM;Fer;set of allowable IDs }ExitRailway }
10385  [W]HH:MM;pas;Location }PassTime }
10386  [W]HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
10387  [W]HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode }
10388  [W]HH:MM;F-nshs;non-repeating headcode }FNSNonRepeatToShuttle }
10389  [W]HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle } Train action entries
10390  [W]HH:MM;Snt-sh;RearStartIdent FrontStartIdent;FSH HeadCode }SNTShuttle }
10391  [W]HH:MM;Sns-sh;FSH HeadCode;F-nshs HeadCode (non-repeating) }SNSShuttle }
10392  [W]HH:MM;Fns-sh;Details }FSHNewService }
10393  [W]HH:MM;Location (arr & dep) }TimeLoc }
10394  [W]HH:MM;HH:MM;Location }TimeTimeLoc }
10395  Command (Frh only) }FinRemHere }
10396 
10397  R;mm;dd;nn. Repeat Repeat entry
10398 
10399  Two times represent arrival & departure, without any other events between (if arrival and departure times are the same
10400  then departure is 30 sec after arrival), single time represents (a) event time; (b) arrival time if train not already
10401  at location; or (c) departure time if train already at location (including train started at location either as a new
10402  train or as a continuation service train at that location). All lines must contain a start entry and a finish entry,
10403  the finish being the last unless there is a repeat entry. The repeat entry begins with 'R', then the incremental
10404  minutes, incremental train headcode last 2 digits, and number of repeats.
10405 
10406  Shuttle entries are where can loop back to an earlier Snt-sh or Sns-sh entry from a Frh-sh or Fns-sh (Finish Shuttle)
10407  entry. Here the shuttle start can have two entries, one from a set position (Snt-sh, must be located) or from a F-nshs
10408  (Sns-sh) - with NO repeat from this source, and from a Fxx-sh, with repeats. After all shuttle repeats Frh-sh remains
10409  where it is, and Fns-sh links to a new service (via an Sns entry), but there must be no repeats in this new service
10410  (it's for a shuttle train to return to depot at end of services)
10411 
10412  Command/Location & details are as follows:-
10413 
10414  Although headcodes can be duplicated, all joins, splits, new services etc give other headcode from both trains' povs, and
10415  these have to match once only, i.e. if 2E44 splits to 2E45 then it can't split to 2E45 anywhere else, and 2E45 must give
10416  2E44 in its Sfs entry. All these are checked.
10417  ***add note re shuttles & their use of otherheadcodes + non-repeating headcodes***
10418 
10419  Start commands:-
10420  Snt (StartNew) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't confuse
10421  with loc as a start entry can't have a location as details)
10422  Sfs (TimeCmdHeadCode) = Start From Split, create a new train that has split from another train (& listed in other train's
10423  timetable line), details = other headcode - (can't confuse with loc as start can't be a loc)
10424  Sns (TimeCmdHeadCode) = Start, headcode change from earlier service - no need to create train as already exists, it just
10425  changes its relevant information, details = old headcode (can't confuse with loc as start can't be a loc)
10426  Snt-sh (SNTShuttle) = Start New Train, i.e. create new train, details = rearstartident, space, frontstartident (can't
10427  confuse with loc as start can't be a loc) then the Fsh-XX service headcode (OtherHeadCode can't be same headcode)
10428  Sns-sh (SNSShuttle) = Start, headcode change from earlier service - no need to create train as already exists, it just
10429  changes its relevant information, details = the FSH-XX service headcode (OtherHeadCode, can't be same headcode)
10430  followed by the non-repeating F-nshs headcode (NonRepeatingShuttleLinkHeadCode)
10431  Sns-fsh (SNSNonRepeatFromShuttle) = Start as a non-repeating service from a shuttle service that has finished all its
10432  repeats, details = NonRepeatingShuttleLinkHeadCode for the corresponding shuttle Fns-sh service
10433 
10434  Intermediate commands:-
10435  Time - Location (TimeLoc), can be arrival or departure depending on context
10436  Time Time location (TimeTimeLoc), arrival and departure
10437  Location Name (exactly as used in the railway) in TimeLoc & TimeTimeLoc means that the train is required to stop at the location
10438  pas (PassTime), Time;pas;Location
10439  jbo (TimeCmdHeadCode) = Joined By Other = joined by other train, details = new headcode (await other train - may be delayed). Note that the
10440  joining train's finish details must correspond or the file check will fail
10441  fsp (TimeCmdHeadCode) = Front Split = a new train splits away from front of this train, both trains in same direction, details = new headcode (create
10442  new train - that train's starting information must correspond)
10443  rsp (TimeCmdHeadCode) = Rear Split = a new train splits away from rear of this train, both trains in same direction, details = new headcode (create
10444  new train - that train's starting information must correspond)
10445  cdt (TimeCmd) = Change Direction of Train = change direction, no details needed & no train creation
10446 
10447  Finish commands:-
10448  Fns (TimeCmdHeadCode) = Finish New Service = finish, form new service in same direction, details = new headcode (no train
10449  creation)
10450  F-nshs (FNSShuttle) = Finish New Service (Shuttle) = finish, form new shuttle service in same direction, details =
10451  shuttle headcode (no train creation)
10452  Fjo (TimeCmdHeadCode) = Finish Join Other = finish, join other train (which must be on an adjacent element, either end -
10453  may have to wait for it), details = new headcode (delete train)
10454  Frh (FinRemHere) = Finish Remain Here = stay here indefinitely, no details & no time needed
10455  Fer (ExitRailway) = Finish, exit railway (i.e at a continuation) - details = set of allowable exit IDs
10456  Frh-sh (TimeCmdHeadCode) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done remain
10457  here
10458  Fns-sh (FSHNewService) = Finish then restart as a shuttle using Snt-sh or Sns-sh, when all shuttle repeats done form new
10459  service via Sns-fsh using the NonRepeatingShuttleLinkHeadCode
10460 
10461  Repeat:-
10462  R;mm;dd;nn (Repeat) where mm = minute increment, dd = 2nd 2 headcode digit increment & nn = no. of repeats (no max as can duplicate
10463  headcodes - it is up to user to avoid duplicates if he/she wishes to.
10464 
10465  Checks carried out with error messages in this function:-
10466  At least one comma in a service line (it's based on a .csv file)
10467  No entries following train information;
10468  At least one comma in remainder after train information (i.e at least a start and a finish entry);
10469  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
10470  First entry not a start entry;
10471  Train information incomplete before a start entry;
10472  Entry follows a finish entry but doesn't begin with 'R';
10473  SplitEntry returns false in a finish entry - message repeats the entry for information;
10474  Last action entry isn't a finish entry.
10475 
10476  Function returns false with no message if:-
10477  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
10478  time is found at all then an error message is given in the calling function);
10479  SplitTrainInfo returns false (message given in called function);
10480  SplitRepeat returns false (message given in called function).
10481 */{
10482  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ProcessOneTimetableLine," + AnsiString(Count) + "," + OneLine + "," +
10483  AnsiString((short)FinalCall) + "," + AnsiString((short)CheckLocationsExistInRailway));
10484  TTrainDataEntry TempTrainDataEntry;
10485 
10486  EndOfFile = false;
10487  StripSpaces(0, OneLine);
10488  // strip both leading and trailing spaces at ends of line and spaces before and after all commas and
10489  // semicolons within the line
10490  ServiceReference = "";
10491  if(OneLine != "")
10492  {
10493  if(OneLine[1] != '*')
10494  {
10495  int SCPos = OneLine.Pos(';');
10496  if(SCPos == 0)
10497  {
10498  ServiceReference = OneLine.SubString(1, 8);
10499  }
10500  else
10501  {
10502  ServiceReference = OneLine.SubString(1, (SCPos - 1));
10503  }
10504  }
10505  }
10506  bool AllCommas = true;
10507 
10508  for(int x = 1; x < OneLine.Length() + 1; x++) // check for nothing but commas (may be all commas if created from Excel) or a blank line
10509  {
10510  if(OneLine[x] != ',')
10511  {
10512  AllCommas = false;
10513  }
10514  }
10515  if(AllCommas || (OneLine == ""))
10516  {
10517  if(Count > 0)
10518  {
10519  EndOfFile = true;
10520  // returns true for a blank line - treated as end of file
10521  Utilities->CallLogPop(1018);
10522  return(true);
10523  }
10524  else // count == 0 so not yet found a start time, no message to be given
10525  {
10526  Utilities->CallLogPop(754);
10527  return(false);
10528  }
10529  }
10530  AnsiString First = "", Second = "", Third = "", Fourth = "";
10531  int RearStartOrRepeatMins = 0, FrontStartOrRepeatDigits = 0, NumberOfRepeats = 0;
10532  TDateTime EventTime(0), ArrivalTime(0), DepartureTime(0);
10533  TDateTime StartTime(0);
10534  TNumList ExitList;
10535  bool Warning = false;
10536 
10537  if(Count == 0) // no start time found yet
10538  {
10539 /* dropped at v0.6b
10540  AnyHeadCodeValid = false;
10541  if(OneLine.SubString(6,5) == ";0000")
10542  {
10543  AnyHeadCodeValid = true;
10544  }
10545 */
10546  if(!CheckTimeValidity(0, OneLine, StartTime))
10547  {
10548  // no message is given for an invalid time as it's assumed to be an irrelevant line
10549  // if no start time is found at all then an error message is given in the calling function
10550  // AnyHeadCodeValid = false;
10551  Utilities->CallLogPop(755);
10552  return(false);
10553  }
10554  if(FinalCall) // here if start time valid
10555  {
10556  TTClockTime = StartTime;
10557  TimetableStartTime = StartTime;
10558  }
10559  }
10560  else
10561  {
10562  AnsiString TrainInfoStr = "", HeadCode = "", Description = "";
10563  int StartSpeed = 0, MaxRunningSpeed = 0, Mass = 0;
10564  double MaxBrakeRate = 0;
10565  double PowerAtRail = 0;
10566  int SignallerSpeed = 0;
10567  if(OneLine[1] == '*')
10568  {
10569  Utilities->CallLogPop(1581);
10570  return(true);
10571  // ignore any line beginning with '*' but return true as there is no error
10572  }
10573  int Pos = OneLine.Pos(',');
10574  if(Pos == 0)
10575  {
10576  int SubStringLength = 20;
10577  if(OneLine.Length() < 20)
10578  {
10579  SubStringLength = OneLine.Length();
10580  }
10581  TimetableMessage(GiveMessages, "Error in timetable - entry incomplete: see '" + OneLine.SubString(1, SubStringLength) + "'....");
10582  Utilities->CallLogPop(766);
10583  return(false);
10584  }
10585  TrainInfoStr = OneLine.SubString(1, Pos - 1);
10586  if(!SplitTrainInfo(0, TrainInfoStr, HeadCode, Description, StartSpeed, MaxRunningSpeed, Mass, MaxBrakeRate, PowerAtRail, SignallerSpeed,
10587  GiveMessages)) // error messages given in SplitTrainInfo
10588  {
10589  Utilities->CallLogPop(773);
10590  return(false);
10591  }
10592  if(FinalCall)
10593  {
10594  // store Train info - conversions done in SplitTrainInfo
10595  // only headcode mandatory for continuing services
10596  TempTrainDataEntry.HeadCode = HeadCode;
10597  TempTrainDataEntry.ServiceReference = HeadCode;
10598  TempTrainDataEntry.Description = Description;
10599  TempTrainDataEntry.StartSpeed = StartSpeed;
10600  TempTrainDataEntry.Mass = Mass;
10601  TempTrainDataEntry.MaxRunningSpeed = MaxRunningSpeed;
10602  TempTrainDataEntry.MaxBrakeRate = MaxBrakeRate;
10603  TempTrainDataEntry.PowerAtRail = PowerAtRail;
10604  TempTrainDataEntry.SignallerSpeed = SignallerSpeed;
10605  TTrainOperatingData TempTrainOperatingData;
10606  TempTrainDataEntry.TrainOperatingDataVector.push_back(TempTrainOperatingData); // push empty vector for now
10607  }
10608  AnsiString NewRemainder = OneLine.SubString(Pos + 1, OneLine.Length() - Pos);
10609  // now left with series of entries for this train, but there may be a string of commas at the end of the line if created by Excel
10610  // so strip them off
10611  while(NewRemainder[NewRemainder.Length()] == ',')
10612  {
10613  if(NewRemainder.Length() > 1)
10614  {
10615  NewRemainder = NewRemainder.SubString(1, NewRemainder.Length() - 1);
10616  }
10617  else
10618  {
10619  NewRemainder = "";
10620  break;
10621  }
10622  }
10623  // check if zero length & fail if so
10624  if(NewRemainder == "")
10625  {
10626  TimetableMessage(GiveMessages, "Error in timetable - no events following train: '" + OneLine + "'");
10627  Utilities->CallLogPop(769);
10628  return(false);
10629  }
10630  // now have one more entry than there are commas
10631  int CommaCount = 0;
10632  for(int x = 1; x < NewRemainder.Length() + 1; x++)
10633  {
10634  if(NewRemainder[x] == ',')
10635  {
10636  CommaCount++;
10637  }
10638  } // must have at least 1 comma, for start & finish entries, unless train is entered under signaller control
10639  if(CommaCount == 0)
10640  {
10641  if((NewRemainder.SubString(7, 3) != "Snt") || (NewRemainder[NewRemainder.Length()] != 'S'))
10642  {
10643  int SubStringLength = 20;
10644  if(OneLine.Length() < 20)
10645  {
10646  SubStringLength = OneLine.Length();
10647  }
10648  TimetableMessage(GiveMessages,
10649  "Error in timetable - must have at least a start and a finish event for a train that is not started under signaller control - see line beginning: '" +
10650  OneLine.SubString(1, SubStringLength) + "'....");
10651  Utilities->CallLogPop(783);
10652  return(false);
10653  }
10654  }
10655  AnsiString OneEntry = "";
10656  TTimetableFormatType FormatType;
10657  TTimetableSequenceType SequenceType;
10658  TTimetableLocationType LocationType;
10659  TTimetableShuttleLinkType ShuttleLinkType;
10660  bool FinishFlag = false;
10661  for(int x = 0; x < CommaCount + 1; x++)
10662  {
10663  if((CommaCount == 0) || (x < CommaCount))
10664  // i.e. train entered under signaller control with no repeats, or entry is not the last,
10665  // in which case there's a comma & finish element or repeat still to come this entry could
10666  // be a finish but can't be a repeat
10667  {
10668  if(CommaCount == 0)
10669  {
10670  OneEntry = NewRemainder;
10671  NewRemainder = "";
10672  }
10673  else
10674  {
10675  Pos = NewRemainder.Pos(',');
10676  OneEntry = NewRemainder.SubString(1, Pos - 1);
10677  NewRemainder = NewRemainder.SubString(Pos + 1, NewRemainder.Length() - Pos);
10678  }
10679  First = "";
10680  Second = "";
10681  Third = "";
10682  Fourth = "";
10683  RearStartOrRepeatMins = 0;
10684  FrontStartOrRepeatDigits = 0;
10685  NumberOfRepeats = 0;
10686  if(!SplitEntry(0, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10687  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10688  {
10689  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10690  Utilities->CallLogPop(756);
10691  return(false);
10692  }
10693  // check if warning for Frh or Fjo & reject
10694  if(Warning && (Second == "Frh"))
10695  {
10696  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry + "': warnings cannot be given for 'Frh' events");
10697  Utilities->CallLogPop(1793);
10698  return(false);
10699  }
10700  if(Warning && (Second == "Fjo"))
10701  {
10702  TimetableMessage(GiveMessages, "Error in line - '" + OneEntry +
10703  "': warnings cannot be given for 'Fjo' events, for a train join warning add a 'W' prefix to the 'jbo' event");
10704  Utilities->CallLogPop(1794);
10705  return(false);
10706  }
10707  if(x == 0) // should be start event
10708  {
10709  if(SequenceType != Start)
10710  {
10711  TimetableMessage(GiveMessages, "Error in timetable - First event not a start event: '" + OneEntry + "'");
10712  Utilities->CallLogPop(784);
10713  return(false);
10714  }
10715  if((Second == "Snt") && (Fourth == 'S') && (NewRemainder != ""))
10716  {
10717  if(NewRemainder[1] != 'R')
10718  {
10719  TimetableMessage(GiveMessages,
10720  "Error in timetable - the only event that can follow a train created under signaller control is a repeat, see '" +
10721  OneEntry + "'");
10722  Utilities->CallLogPop(787);
10723  return(false);
10724  }
10725  }
10726  if((Second == "Snt") || (Second == "Snt-sh"))
10727  // need full train information including non-default values for at least HeadCode, Description,
10728  // MaxRunningSpeed, Mass, MaxBrakeRate, & PowerAtRail
10729  {
10730  if((HeadCode == "") || (Description == "") || (MaxRunningSpeed == 0) || (Mass == 0) || (MaxBrakeRate == 0)) // ||
10731  // (PowerAtRail == 0)) allowed 0 for power at v2.4.0
10732  {
10733  TimetableMessage(GiveMessages, "Error in timetable - train information incomplete before 'Snt' or 'Snt-sh' start event: '" +
10734  OneEntry + "'");
10735  Utilities->CallLogPop(1783);
10736  return(false);
10737  }
10738  }
10739  if((Second == "Sfs") || (Second == "Sns") || (Second == "Sns-sh") || (Second == "Sns-fsh"))
10740  // service continuation - need at least non-default value for HeadCode
10741  {
10742  if(HeadCode == "")
10743  {
10744  TimetableMessage(GiveMessages, "Error in timetable - headcode missing before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10745  OneEntry + "'");
10746  Utilities->CallLogPop(788);
10747  return(false);
10748  }
10749  if((StartSpeed != 0) || (MaxRunningSpeed != 0) || (Mass != 0) || (MaxBrakeRate != 0) || (PowerAtRail != 0))
10750  {
10751  TimetableMessage(GiveMessages,
10752  "Error in timetable - information additional to a headcode & optional description given before 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' start event: '" +
10753  OneEntry + "'");
10754  Utilities->CallLogPop(843);
10755  return(false);
10756  }
10757  }
10758  }
10759  if(SequenceType == Finish)
10760  {
10761  FinishFlag = true;
10762  // marker for only permitted additional entry being a repeat, only needed if the
10763  // finish entry is not the last entry
10764  }
10765  if(FinalCall)
10766  {
10767  // interpret & add to ActionVector
10768  TDateTime TempTime;
10769  TActionVectorEntry ActionVectorEntry;
10770  ActionVectorEntry.FormatType = FormatType;
10771  ActionVectorEntry.LocationType = LocationType;
10772  ActionVectorEntry.SequenceType = SequenceType;
10773  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10774  ActionVectorEntry.Warning = Warning;
10775  if(FormatType == TimeLoc)
10776  {
10777  if(CheckTimeValidity(1, First, ActionVectorEntry.EventTime))
10778  {
10779  ;
10780  } // these will all be true as final call
10781 
10782  ActionVectorEntry.LocationName = Second;
10783  }
10784  else if(FormatType == PassTime) // new
10785  {
10786  if(CheckTimeValidity(17, First, ActionVectorEntry.EventTime))
10787  {
10788  ;
10789  }
10790  ActionVectorEntry.Command = Second;
10791  ActionVectorEntry.LocationName = Third;
10792  }
10793  else if(FormatType == TimeTimeLoc)
10794  {
10795  if(CheckTimeValidity(2, First, ActionVectorEntry.ArrivalTime))
10796  {
10797  ;
10798  }
10799  if(CheckTimeValidity(3, Second, ActionVectorEntry.DepartureTime))
10800  {
10801  ;
10802  }
10803  ActionVectorEntry.LocationName = Third;
10804  }
10805  else if(FormatType == TimeCmd)
10806  {
10807  if(CheckTimeValidity(4, First, ActionVectorEntry.EventTime))
10808  {
10809  ;
10810  }
10811  ActionVectorEntry.Command = Second;
10812  }
10813  else if(FormatType == ExitRailway)
10814  {
10815  if(CheckTimeValidity(18, First, ActionVectorEntry.EventTime))
10816  {
10817  ;
10818  }
10819  ActionVectorEntry.Command = Second;
10820  ActionVectorEntry.ExitList = ExitList;
10821  }
10822  else if(FormatType == StartNew)
10823  {
10824  if(CheckTimeValidity(5, First, ActionVectorEntry.EventTime))
10825  {
10826  ;
10827  }
10828  ActionVectorEntry.Command = Second;
10829  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10830  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10831  if(Fourth == 'S')
10832  {
10833  ActionVectorEntry.SignallerControl = true;
10834  }
10835  }
10836  else if(FormatType == SNTShuttle)
10837  {
10838  if(CheckTimeValidity(6, First, ActionVectorEntry.EventTime))
10839  {
10840  ;
10841  }
10842  ActionVectorEntry.Command = Second;
10843  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
10844  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
10845  ActionVectorEntry.OtherHeadCode = Fourth;
10846  }
10847  else if(FormatType == SNSShuttle)
10848  {
10849  if(CheckTimeValidity(7, First, ActionVectorEntry.EventTime))
10850  {
10851  ;
10852  }
10853  ActionVectorEntry.Command = Second;
10854  ActionVectorEntry.OtherHeadCode = Third;
10855  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10856  }
10857  else if(FormatType == TimeCmdHeadCode)
10858  {
10859  if(CheckTimeValidity(8, First, ActionVectorEntry.EventTime))
10860  {
10861  ;
10862  }
10863  ActionVectorEntry.Command = Second;
10864  ActionVectorEntry.OtherHeadCode = Third;
10865  }
10866  else if((FormatType == FNSNonRepeatToShuttle) || (FormatType == SNSNonRepeatFromShuttle))
10867  {
10868  if(CheckTimeValidity(9, First, ActionVectorEntry.EventTime))
10869  {
10870  ;
10871  }
10872  ActionVectorEntry.Command = Second;
10873  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10874  }
10875  else if(FormatType == FSHNewService)
10876  {
10877  if(CheckTimeValidity(10, First, ActionVectorEntry.EventTime))
10878  {
10879  ;
10880  }
10881  ActionVectorEntry.Command = Second;
10882  ActionVectorEntry.OtherHeadCode = Third;
10883  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10884  }
10885  else if(FormatType == FinRemHere)
10886  {
10887  ActionVectorEntry.Command = Second;
10888  }
10889  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10890  }
10891  }
10892  else // last entry, & not entered under signaller control with no repeats, i.e. could be finish or repeat
10893  {
10894  OneEntry = NewRemainder;
10895  First = "";
10896  Second = "";
10897  Third = "";
10898  Fourth = "";
10899  RearStartOrRepeatMins = 0;
10900  FrontStartOrRepeatDigits = 0;
10901  NumberOfRepeats = 0;
10902  if((FinishFlag) && (OneEntry[1] != 'R'))
10903  // already had a finish entry
10904  {
10905  TimetableMessage(GiveMessages, "Error in timetable - Last event = '" + OneEntry + "'. An earlier finish event has been found with something other than a repeat following it - only a repeat can follow a finish event.");
10906  Utilities->CallLogPop(79);
10907  return(false);
10908  }
10909  if(OneEntry[1] != 'R') // must be finish
10910  {
10911  if(!SplitEntry(1, OneEntry, GiveMessages, CheckLocationsExistInRailway, First, Second, Third, Fourth, RearStartOrRepeatMins,
10912  FrontStartOrRepeatDigits, FormatType, LocationType, SequenceType, ShuttleLinkType, ExitList, Warning))
10913  {
10914  TimetableMessage(GiveMessages, "Error in timetable - Event: '" + OneEntry + "'");
10915  Utilities->CallLogPop(757);
10916  return(false);
10917  }
10918  if(SequenceType != Finish)
10919  {
10920  TimetableMessage(GiveMessages, "Error in timetable - last event should be a finish: '" + OneEntry + "'");
10921  Utilities->CallLogPop(785);
10922  return(false);
10923  }
10924  if(FinalCall)
10925  {
10926  // interpret & add to ActionVector
10927  TDateTime TempTime;
10928  TActionVectorEntry ActionVectorEntry;
10929  ActionVectorEntry.FormatType = FormatType;
10930  ActionVectorEntry.LocationType = LocationType;
10931  ActionVectorEntry.SequenceType = SequenceType;
10932  ActionVectorEntry.ShuttleLinkType = ShuttleLinkType;
10933  ActionVectorEntry.Warning = Warning;
10934  if(FormatType == TimeCmd)
10935  {
10936  if(CheckTimeValidity(11, First, ActionVectorEntry.EventTime))
10937  {
10938  ;
10939  }
10940  ActionVectorEntry.Command = Second;
10941  }
10942  else if(FormatType == TimeCmdHeadCode)
10943  {
10944  if(CheckTimeValidity(12, First, ActionVectorEntry.EventTime))
10945  {
10946  ;
10947  }
10948  ActionVectorEntry.Command = Second;
10949  ActionVectorEntry.OtherHeadCode = Third;
10950  }
10951  else if(FormatType == FNSNonRepeatToShuttle)
10952  {
10953  if(CheckTimeValidity(13, First, ActionVectorEntry.EventTime))
10954  {
10955  ;
10956  }
10957  ActionVectorEntry.Command = Second;
10958  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Third;
10959  }
10960  else if(FormatType == FSHNewService)
10961  {
10962  if(CheckTimeValidity(14, First, ActionVectorEntry.EventTime))
10963  {
10964  ;
10965  }
10966  ActionVectorEntry.Command = Second;
10967  ActionVectorEntry.OtherHeadCode = Third;
10968  ActionVectorEntry.NonRepeatingShuttleLinkHeadCode = Fourth;
10969  }
10970  else if(FormatType == ExitRailway)
10971  {
10972  if(CheckTimeValidity(19, First, ActionVectorEntry.EventTime))
10973  {
10974  ;
10975  }
10976  ActionVectorEntry.Command = Second;
10977  ActionVectorEntry.ExitList = ExitList;
10978  }
10979  else if(FormatType == FinRemHere)
10980  {
10981  ActionVectorEntry.Command = Second;
10982  }
10983  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
10984  }
10985  }
10986  else // repeat
10987  {
10988  if(!SplitRepeat(0, OneEntry, RearStartOrRepeatMins, FrontStartOrRepeatDigits, NumberOfRepeats, GiveMessages))
10989  {
10990  Utilities->CallLogPop(786);
10991  // error messages given in SplitRepeat
10992  return(false);
10993  }
10994  if(FinalCall)
10995  {
10996  TActionVectorEntry ActionVectorEntry;
10997  ActionVectorEntry.FormatType = Repeat;
10998  ActionVectorEntry.LocationType = LocTypeForRepeatEntry;
10999  ActionVectorEntry.SequenceType = SequTypeForRepeatEntry;
11000  ActionVectorEntry.ShuttleLinkType = ShuttleLinkTypeForRepeatEntry;
11001  ActionVectorEntry.RearStartOrRepeatMins = RearStartOrRepeatMins;
11002  ActionVectorEntry.FrontStartOrRepeatDigits = FrontStartOrRepeatDigits;
11003  ActionVectorEntry.NumberOfRepeats = NumberOfRepeats;
11004  TempTrainDataEntry.ActionVector.push_back(ActionVectorEntry);
11005  }
11006  }
11007  }
11008  }
11009  if(FinalCall)
11010  {
11011  TrainDataVector.push_back(TempTrainDataEntry);
11012  }
11013  }
11014  Utilities->CallLogPop(80);
11015  return(true);
11016 }
11017 
11018 // ---------------------------------------------------------------------------
11019 
11020 bool TTrainController::Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
11021 {
11022  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",Last2CharactersBothDigits," + HeadCode);
11023  if((HeadCode[HeadCode.Length() - 1] < '0') || (HeadCode[HeadCode.Length() - 1] > '9'))
11024  {
11025  Utilities->CallLogPop(1890);
11026  return(false);
11027  }
11028  if((HeadCode[HeadCode.Length()] < '0') || (HeadCode[HeadCode.Length()] > '9'))
11029  {
11030  Utilities->CallLogPop(1891);
11031  return(false);
11032  }
11033  Utilities->CallLogPop(1892);
11034  return(true);
11035 }
11036 
11037 // ---------------------------------------------------------------------------
11038 
11039 bool TTrainController::CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
11040 // 1st 5 chars must be HH:MM, anything else will be ignored
11041 {
11042  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckTimeValidity," + TimeStr);
11043  if(TimeStr.Length() < 5)
11044  {
11045  Utilities->CallLogPop(926);
11046  return(false);
11047  }
11048  if((TimeStr[1] < '0') || (TimeStr[1] > '9'))
11049  {
11050  Utilities->CallLogPop(927);
11051  return(false);
11052  }
11053  if((TimeStr[2] < '0') || (TimeStr[2] > '9'))
11054  {
11055  Utilities->CallLogPop(928);
11056  return(false);
11057  }
11058  if(TimeStr[3] != ':')
11059  {
11060  Utilities->CallLogPop(929);
11061  return(false);
11062  }
11063  if((TimeStr[4] < '0') || (TimeStr[4] > '5'))
11064  {
11065  Utilities->CallLogPop(930);
11066  return(false);
11067  }
11068  if((TimeStr[5] < '0') || (TimeStr[5] > '9'))
11069  {
11070  Utilities->CallLogPop(931);
11071  return(false);
11072  }
11073  while(TimeStr.Length() > 5)
11074  {
11075  TimeStr = TimeStr.SubString(1, TimeStr.Length() - 1);
11076  }
11077  double WholeHours = (AnsiString(TimeStr[1]) + AnsiString(TimeStr[2])).ToDouble();
11078  double FracHour = ((AnsiString(TimeStr[4]) + AnsiString(TimeStr[5])).ToDouble()) / 60.0;
11079 
11080  if((WholeHours + FracHour) >= 95.98334)
11081  {
11082  Utilities->CallLogPop(1817);
11083  return(false); // > 95h 59m
11084  }
11085  Time = TDateTime((WholeHours + FracHour) / 24);
11086  Utilities->CallLogPop(932);
11087  return(true);
11088 }
11089 
11090 // ---------------------------------------------------------------------------
11091 
11092 bool TTrainController::SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second,
11093  AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, TTimetableFormatType &FormatType,
11094  TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
11095 /* This is a train action entry from a single line of the timetable, i.e. not train information and not a repeat entry.
11096  Return false for failure.
11097  See description above under ProcessOneTimetableLinefor details of train action entries
11098  NB all types set except LocationType for Sns as may be located or not
11099 */{
11100  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitEntry," + OneEntry);
11101  Warning = false;
11102  TDateTime TempTime;
11103 
11104  if(OneEntry.Length() > 0)
11105  {
11106  if(OneEntry[1] == 'W') // warning
11107  {
11108  Warning = true;
11109  OneEntry = OneEntry.SubString(2, OneEntry.Length() - 1);
11110  // strip it off
11111  }
11112  }
11113  if(OneEntry == "Frh")
11114  {
11115  FormatType = FinRemHere;
11116  SequenceType = Finish;
11117  LocationType = AtLocation;
11118  ShuttleLinkType = NotAShuttleLink;
11119  Second = "Frh";
11120  Utilities->CallLogPop(1016);
11121  return(true);
11122  }
11123  if(OneEntry.Length() < 7)
11124  {
11125  Utilities->CallLogPop(907);
11126  return(false); // 'HH:MM;' + at least a one-letter location name
11127  }
11128  int Pos = OneEntry.Pos(';'); // first segment delimiter
11129 
11130  if(Pos != 6)
11131  {
11132  Utilities->CallLogPop(908);
11133  return(false);
11134  // no delimiter or delimiter not in position 6, has to be a time so fail
11135  }
11136  First = OneEntry.SubString(1, 5); // has to be a time
11137  if(!CheckTimeValidity(16, First, TempTime))
11138  {
11139  Utilities->CallLogPop(909);
11140  return(false);
11141  }
11142  AnsiString Remainder = OneEntry.SubString(Pos + 1, OneEntry.Length() - Pos);
11143 
11144  if((Remainder[1] >= '0') && (Remainder[1] <= '9'))
11145  // next segment is a time so this is a TimeTimeLoc & 3rd seg has to be a location to be valid
11146  {
11147  if(Remainder.Length() < 7)
11148  {
11149  Utilities->CallLogPop(910);
11150  return(false); // 'HH:MM;' + at least a one-letter location name
11151  }
11152  Pos = Remainder.Pos(';'); // second segment delimiter
11153  if(Pos == 0)
11154  {
11155  Utilities->CallLogPop(911);
11156  return(false);
11157  // no delimiter, has to be one between departure time & location
11158  }
11159  Second = Remainder.SubString(1, 5); // has to be a time
11160  if(!CheckTimeValidity(15, Second, TempTime))
11161  {
11162  Utilities->CallLogPop(912);
11163  return(false);
11164  }
11165  Third = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11166  if(!CheckLocationValidity(0, Third, GiveMessages, CheckLocationsExistInRailway))
11167  {
11168  Utilities->CallLogPop(913);
11169  return(false);
11170  }
11171  FormatType = TimeTimeLoc;
11172  SequenceType = Intermediate;
11173  LocationType = AtLocation;
11174  ShuttleLinkType = NotAShuttleLink;
11175  Utilities->CallLogPop(914);
11176  return(true);
11177  }
11178  Pos = Remainder.Pos(';'); // second segment delimiter
11179  if(Pos == 0) // no third segment so second must be a location, or cdt
11180  {
11181  Second = Remainder;
11182  if(Second == "cdt")
11183  {
11184  FormatType = TimeCmd;
11185  ShuttleLinkType = NotAShuttleLink;
11186  LocationType = AtLocation;
11187  SequenceType = Intermediate;
11188  Utilities->CallLogPop(915);
11189  return(true);
11190  }
11191  if(!CheckLocationValidity(1, Second, GiveMessages, CheckLocationsExistInRailway))
11192  {
11193  Utilities->CallLogPop(916);
11194  return(false);
11195  }
11196  else
11197  {
11198  FormatType = TimeLoc;
11199  LocationType = AtLocation;
11200  SequenceType = Intermediate;
11201  ShuttleLinkType = NotAShuttleLink;
11202  Utilities->CallLogPop(917);
11203  return(true);
11204  }
11205  }
11206  // here if second segment is a command, with a third & maybe fourth segments as details
11207  if((Pos != 4) && (Pos != 7) && (Pos != 8))
11208  {
11209  Utilities->CallLogPop(918);
11210  return(false);
11211  // no third segement or not in position 4 or 7, & should be since all commands are 3, 6 or 7 letters
11212  }
11213  Second = Remainder.SubString(1, Pos - 1); // command
11214 
11215  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11216  // details
11217  Pos = Remainder.Pos(';'); // third segment delimiter
11218  if(Pos == 0)
11219  {
11220  Third = Remainder;
11221  }
11222  else
11223  {
11224  Third = Remainder.SubString(1, Pos - 1);
11225  Fourth = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11226  }
11227  if((Second == "Snt") || (Second == "Snt-sh"))
11228  // third has to be 2 element idents with a space between
11229  {
11230  int SpacePos = Third.Pos(' ');
11231  if(SpacePos == 0)
11232  {
11233  Utilities->CallLogPop(919);
11234  return(false); // no space
11235  }
11236  AnsiString RearStartStr = Third.SubString(1, SpacePos - 1);
11237  AnsiString FrontStartStr = Third.SubString(SpacePos + 1, Third.Length() - SpacePos);
11238  // int RearPosition=0, FrontPosition=0, RearExitPos=0;
11239  if(CheckLocationsExistInRailway)
11240  {
11241  if(!CheckStartPositionValidity(0, RearStartStr, FrontStartStr, GiveMessages))
11242  {
11243  Utilities->CallLogPop(920);
11244  return(false);
11245  }
11246  RearStartOrRepeatMins = Track->GetTrackVectorPositionFromString(3, RearStartStr, GiveMessages);
11247  FrontStartOrRepeatDigits = Track->GetTrackVectorPositionFromString(4, FrontStartStr, GiveMessages);
11248  }
11249  if(Second == "Snt")
11250  {
11251  FormatType = StartNew;
11252  SequenceType = Start;
11253  LocationType = NoLocation;
11254  // can't be set until know whether located or not - done in SecondPassActions
11255  ShuttleLinkType = NotAShuttleLink;
11256  }
11257  else // Snt-sh
11258  {
11259  FormatType = SNTShuttle;
11260  LocationType = AtLocation;
11261  SequenceType = Start;
11262  ShuttleLinkType = ShuttleLink;
11263  if(!CheckHeadCodeValidity(0, GiveMessages, Fourth))
11264  {
11265  Utilities->CallLogPop(1038);
11266  return(false);
11267  }
11268  }
11269  Utilities->CallLogPop(921);
11270  return(true);
11271  }
11272  if(Second == "Sns-sh") // third & fourth have to be headcodes
11273  {
11274  FormatType = SNSShuttle;
11275  LocationType = AtLocation;
11276  SequenceType = Start;
11277  ShuttleLinkType = ShuttleLink;
11278  if(!CheckHeadCodeValidity(1, GiveMessages, Third))
11279  {
11280  Utilities->CallLogPop(1039);
11281  return(false);
11282  }
11283  if(!CheckHeadCodeValidity(2, GiveMessages, Fourth))
11284  {
11285  Utilities->CallLogPop(1040);
11286  return(false);
11287  }
11288  Utilities->CallLogPop(1041);
11289  return(true);
11290  }
11291  if(Second == "F-nshs")
11292  {
11293  FormatType = FNSNonRepeatToShuttle;
11294  LocationType = AtLocation;
11295  SequenceType = Finish;
11296  ShuttleLinkType = ShuttleLink;
11297  if(!CheckHeadCodeValidity(3, GiveMessages, Third))
11298  {
11299  Utilities->CallLogPop(1047);
11300  return(false);
11301  }
11302  Utilities->CallLogPop(1048);
11303  return(true);
11304  }
11305  if(Second == "Sns-fsh")
11306  {
11307  FormatType = SNSNonRepeatFromShuttle;
11308  LocationType = AtLocation;
11309  SequenceType = Start;
11310  ShuttleLinkType = ShuttleLink;
11311  if(!CheckHeadCodeValidity(4, GiveMessages, Third))
11312  {
11313  Utilities->CallLogPop(1098);
11314  return(false);
11315  }
11316  Utilities->CallLogPop(1099);
11317  return(true);
11318  }
11319  if(Second == "Fns-sh") // third & fourth have to be headcodes
11320  {
11321  FormatType = FSHNewService;
11322  LocationType = AtLocation;
11323  SequenceType = Finish;
11324  ShuttleLinkType = ShuttleLink;
11325  if(!CheckHeadCodeValidity(5, GiveMessages, Third))
11326  {
11327  Utilities->CallLogPop(1050);
11328  return(false);
11329  }
11330  if(!CheckHeadCodeValidity(6, GiveMessages, Fourth))
11331  {
11332  Utilities->CallLogPop(1051);
11333  return(false);
11334  }
11335  Utilities->CallLogPop(1052);
11336  return(true);
11337  }
11338  // new segment for 'pas'
11339  if(Second == "pas") // third has to be a location
11340  {
11341  FormatType = PassTime;
11342  LocationType = EnRoute;
11343  SequenceType = Intermediate;
11344  ShuttleLinkType = NotAShuttleLink;
11345  if(!CheckLocationValidity(2, Third, GiveMessages, CheckLocationsExistInRailway))
11346  {
11347  Utilities->CallLogPop(1515);
11348  return(false);
11349  }
11350  Utilities->CallLogPop(1516);
11351  return(true);
11352  }
11353  // new segment for revised 'Fer'
11354  if(Second == "Fer")
11355  // third has to be a set of IDs separated by spaces, and at least 1
11356  {
11357  FormatType = ExitRailway;
11358  LocationType = EnRoute;
11359  SequenceType = Finish;
11360  ShuttleLinkType = NotAShuttleLink;
11361  if(CheckLocationsExistInRailway)
11362  {
11363  if(!CheckAndPopulateListOfIDs(0, Third, ExitList, GiveMessages))
11364  {
11365  Utilities->CallLogPop(1519);
11366  return(false);
11367  }
11368  }
11369  Utilities->CallLogPop(1520);
11370  return(true);
11371  }
11372  // all remainder must be TimeCmdHeadCode types to be valid
11373  if((Second != "Fns") && (Second != "Fjo") && (Second != "jbo") && (Second != "fsp") && (Second != "rsp") && (Second != "Sfs") && (Second != "Sns") &&
11374  (Second != "Frh-sh"))
11375  {
11376  Utilities->CallLogPop(922);
11377  return(false); // all TimeCmdHeadCode types
11378  }
11379  if(!CheckHeadCodeValidity(7, GiveMessages, Third))
11380  {
11381  Utilities->CallLogPop(923);
11382  return(false);
11383  }
11384  FormatType = TimeCmdHeadCode;
11385  LocationType = AtLocation;
11386  if(Second == "Frh-sh")
11387  {
11388  ShuttleLinkType = ShuttleLink;
11389  }
11390  else
11391  {
11392  ShuttleLinkType = NotAShuttleLink;
11393  }
11394  if((Second == "Fns") || (Second == "Fjo") || (Second == "Frh-sh"))
11395  {
11396  SequenceType = Finish;
11397  }
11398  if((Second == "jbo") || (Second == "fsp") || (Second == "rsp"))
11399  {
11400  SequenceType = Intermediate;
11401  }
11402  if((Second == "Sfs") || (Second == "Sns"))
11403  {
11404  SequenceType = Start;
11405  }
11406  Utilities->CallLogPop(924);
11407  return(true);
11408 }
11409 
11410 // ---------------------------------------------------------------------------
11411 
11412 bool TTrainController::CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
11413 {
11414  // check that the location name exists in the railway (only if CheckLocationsExistInRailway is true), doesn't begin with a number
11415  // and contains no special characters
11416  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckLocationValidity," + LocStr);
11417  if(LocStr == "")
11418  {
11419  Utilities->CallLogPop(1353);
11420  return(false); // has to have at least one character
11421  }
11422  if((LocStr[1] >= '0') && (LocStr[1] <= '9'))
11423  {
11424  Utilities->CallLogPop(1354);
11425  return(false); // can't begin with a number
11426  }
11427  for(int x = 1; x < LocStr.Length() + 1; x++)
11428  {
11429  if(LocStr[x] < ' ')
11430  {
11431  Utilities->CallLogPop(1355);
11432  return(false); // contains a special character
11433  }
11434  if(LocStr[x] > 'z')
11435  {
11436  Utilities->CallLogPop(1356);
11437  return(false); // contains a character outside the standard ASCII set
11438  }
11439  }
11440  // check exists in railway location list if CheckLocationsExistInRailway is true
11441  if(CheckLocationsExistInRailway)
11442  {
11443  if(!Track->TimetabledLocationNameAllocated(3, LocStr))
11444  {
11445  TimetableMessage(GiveMessages, "Location name '" + LocStr +
11446  "' appears in the timetable but is not a valid name. To be valid the name must be a stopping location and apply to one or more platforms " +
11447  "(not concourses on their own), or to track at a blue non-station named location. BUT NOTE THAT trains can't stop at continuations so a name " +
11448  "that includes a continuation will not be valid.");
11449  Utilities->CallLogPop(1357);
11450  return(false);
11451  }
11452  }
11453  Utilities->CallLogPop(1358);
11454  return(true);
11455 }
11456 
11457 // ---------------------------------------------------------------------------
11458 
11459 bool TTrainController::CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
11460 {
11461  // if(!AnyHeadCodeValid) up to 8 characters total & last 4 characters must be NLNN where N = number and L = capital or small letter
11462  // if(AnyHeadCodeValid) up to 8 characters total, last 2 chars must be digits & last but 2 can be any alphanumeric, upper or lower case
11463  // NOTE: As of v0.6b AnyHeadCodeValid dropped, all headcodes are unrestricted
11464  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + "," + AnsiString((short)GiveMessages) + ",CheckHeadCodeValidity," +
11465  HeadCode);
11466  if((HeadCode.Length() < 4) || (HeadCode.Length() > 8))
11467  {
11468  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode +
11469  "', length must be between 4 and 8 characters, and last 4 must be a legitimate headcode. This error can also be caused by omitting a service reference after Sns, Snt-sh, Sns-sh, Fns, Fns-sh or Frh-sh");
11470  Utilities->CallLogPop(1359);
11471  return(false);
11472  }
11473  // firstly allow any printable character (ASCII >= CHAR(32) & <= CHAR(126)), as these allowed in 1st 4 characters
11474  for(int x = 1; x < (HeadCode.Length() + 1); x++)
11475  {
11476  if((HeadCode[x] < ' ') || (HeadCode[x] > '~'))
11477  {
11478  TimetableMessage(GiveMessages, "Non-printable character in headcode '" + HeadCode + "'");
11479  Utilities->CallLogPop(1895);
11480  return(false);
11481  }
11482  }
11483  // secondly ensure the true Headcode only has letters or digits
11484  for(int x = 3; x >= 0; x--)
11485  {
11486  if(((HeadCode[HeadCode.Length() - x] < 'A') || (HeadCode[HeadCode.Length() - x] > 'Z')) && ((HeadCode[HeadCode.Length() - x] < 'a') ||
11487  (HeadCode[HeadCode.Length() - x] > 'z')) && ((HeadCode[HeadCode.Length() - x] < '0') || (HeadCode[HeadCode.Length() - x] > '9')))
11488  {
11489  TimetableMessage(GiveMessages, "Headcode error in '" + HeadCode + "', headcode must consist of letters and digits only");
11490  Utilities->CallLogPop(1790);
11491  return(false);
11492  }
11493  }
11494  Utilities->CallLogPop(1364);
11495  return(true);
11496 }
11497 
11498 // ---------------------------------------------------------------------------
11499 
11500 bool TTrainController::CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
11501 // set of legitimate track element IDs, separated by spaces, and at least 1 present
11502 {
11503  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSetOfIDs," + IDSet);
11504  ExitList.clear();
11505  AnsiString CurrentID = "";
11506 
11507  if(IDSet.Length() == 0)
11508  {
11509  TimetableMessage(GiveMessages, "Must have at least one exit element ID following 'Fer'");
11510  Utilities->CallLogPop(1521);
11511  return(false);
11512  }
11513  for(int x = 1; x <= IDSet.Length(); x++)
11514  {
11515  char C = IDSet[x];
11516  if(((C < '0') || (C > '9')) && (C != ' ') && (C != 'N') && (C != '-'))
11517  {
11518  TimetableMessage(GiveMessages, "Illegal character in the set of element IDs following 'Fer' in '" + IDSet + "'");
11519  Utilities->CallLogPop(1522);
11520  return(false);
11521  }
11522  }
11523  int Pos = IDSet.Pos(' '); // look for the first space
11524 
11525  while(true)
11526  {
11527  if(Pos == 0)
11528  {
11529  CurrentID = IDSet;
11530  IDSet = "";
11531  }
11532  else
11533  {
11534  CurrentID = IDSet.SubString(1, Pos - 1);
11535  IDSet = IDSet.SubString(Pos + 1, IDSet.Length() - Pos);
11536  }
11537  int VecPos = Track->GetTrackVectorPositionFromString(7, CurrentID, GiveMessages);
11538  if(VecPos == -1)
11539  {
11540  Utilities->CallLogPop(1523);
11541  return(false); // messages given in GetTrackVectorPositionFromString
11542  }
11543  else
11544  {
11545  if(Track->TrackElementAt(722, VecPos).TrackType != Continuation)
11546  {
11547  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' is not an exit");
11548  Utilities->CallLogPop(1524);
11549  return(false);
11550  }
11551  else
11552  {
11553  // first check for duplicates
11554  if(!ExitList.empty())
11555  {
11556  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
11557  {
11558  if(*ELIT == VecPos)
11559  {
11560  TimetableMessage(GiveMessages, "The element ID '" + CurrentID + "' following 'Fer' duplicates an earlier element");
11561  Utilities->CallLogPop(1532);
11562  return(false);
11563  }
11564  }
11565  }
11566  // of OK add it to the list
11567  ExitList.push_back(VecPos);
11568  }
11569  }
11570  if(IDSet == "")
11571  {
11572  Utilities->CallLogPop(1525);
11573  return(true);
11574  }
11575  else
11576  {
11577  Pos = IDSet.Pos(' '); // look for the next space
11578  }
11579  } // while(true)
11580 }
11581 
11582 // ---------------------------------------------------------------------------
11583 bool TTrainController::SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed,
11584  int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
11585 // 7 or 8 items for a new train (6 or 7 semicolons), for a continuing service only need headcode, though can have a description, if other
11586 // data entered for continuing service then will be ignored - message given to warn user, checks appropriate number of items and validity
11587 // of each item
11588 {
11589  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitTrainInfo," + TrainInfoStr);
11590  int Pos = 0;
11591  AnsiString Remainder = "";
11592  int SemiColonCount = 0;
11593 
11594  for(int x = 1; x < TrainInfoStr.Length() + 1; x++)
11595  {
11596  if(TrainInfoStr[x] == ';')
11597  {
11598  SemiColonCount++;
11599  }
11600  }
11601  if((SemiColonCount != 6) && (SemiColonCount != 7) && (SemiColonCount != 1) && (SemiColonCount != 0))
11602  {
11603  TimetableMessage(GiveMessages, "Error in train information in '" + TrainInfoStr +
11604  "'. Should be headcode + optional description for a continuing service;" +
11605  " or headcode, description, start speed, max running speed, mass, brake force, power (and optional signaller max. speed) for a new service");
11606  Utilities->CallLogPop(880);
11607  return(false);
11608  }
11609  if(SemiColonCount == 0)
11610  {
11611  HeadCode = TrainInfoStr;
11612  if(!CheckHeadCodeValidity(8, GiveMessages, HeadCode))
11613  {
11614  Utilities->CallLogPop(881);
11615  return(false);
11616  }
11617  Utilities->CallLogPop(882);
11618  return(true);
11619  }
11620  if(SemiColonCount == 1) // headcode & description only
11621  {
11622  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11623  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11624  Description = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11625  if(!CheckHeadCodeValidity(9, GiveMessages, HeadCode))
11626  {
11627  Utilities->CallLogPop(883);
11628  return(false);
11629  }
11630  if(Description == "")
11631  {
11632  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11633  Utilities->CallLogPop(884);
11634  return(false);
11635  }
11636  if(Description.Length() > 60)
11637  {
11638  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11639  Utilities->CallLogPop(1157);
11640  return(false);
11641  }
11642  for(int x = 1; x < Description.Length() + 1; x++)
11643  {
11644  if((Description[x] < ' ') || (Description[x] > '~'))
11645  {
11646  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11647  Utilities->CallLogPop(885);
11648  return(false);
11649  }
11650  }
11651  Utilities->CallLogPop(886);
11652  return(true);
11653  }
11654  // if here must have 6 or 7 semicolons
11655  Pos = TrainInfoStr.Pos(';'); // 1st delimiter
11656  HeadCode = TrainInfoStr.SubString(1, Pos - 1);
11657  Remainder = TrainInfoStr.SubString(Pos + 1, TrainInfoStr.Length() - Pos);
11658  if(!CheckHeadCodeValidity(10, GiveMessages, HeadCode))
11659  {
11660  Utilities->CallLogPop(887);
11661  return(false);
11662  }
11663  Pos = Remainder.Pos(';'); // 2nd delimiter
11664  Description = Remainder.SubString(1, Pos - 1);
11665  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11666  if(Description == "")
11667  {
11668  TimetableMessage(GiveMessages, "Train description missing in '" + TrainInfoStr + "'");
11669  Utilities->CallLogPop(888);
11670  return(false);
11671  }
11672  if(Description.Length() > 60)
11673  {
11674  TimetableMessage(GiveMessages, "Train description too long, limit of 60 characters '" + TrainInfoStr + "'");
11675  Utilities->CallLogPop(1158);
11676  return(false);
11677  }
11678  for(int x = 1; x < Description.Length() + 1; x++)
11679  {
11680  if((Description[x] < ' ') || (Description[x] > 126))
11681  {
11682  TimetableMessage(GiveMessages, "Train description contains invalid characters in '" + TrainInfoStr + "'");
11683  Utilities->CallLogPop(889);
11684  return(false);
11685  }
11686  }
11687  Pos = Remainder.Pos(';'); // 3rd delimiter
11688  AnsiString StartSpeedStr = Remainder.SubString(1, Pos - 1);
11689 
11690  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11691  if(StartSpeedStr == "")
11692  {
11693  TimetableMessage(GiveMessages, "Train starting speed missing in '" + TrainInfoStr + "'");
11694  Utilities->CallLogPop(890);
11695  return(false);
11696  }
11697  for(int x = 1; x < StartSpeedStr.Length() + 1; x++)
11698  {
11699  if((StartSpeedStr[x] < '0') || (StartSpeedStr[x] > '9'))
11700  {
11701  TimetableMessage(GiveMessages, "Train start speed contains invalid characters in '" + TrainInfoStr + "'");
11702  Utilities->CallLogPop(891);
11703  return(false);
11704  }
11705  }
11706  StartSpeed = StartSpeedStr.ToInt();
11707  if(StartSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11708  {
11709  StartSpeed = TTrain::MaximumSpeedLimit;
11710  if(!SSHigh) // added at v2.4.0
11711  {
11712  TimetableMessage(GiveMessages, "Train starting speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11713  SSHigh = true;
11714  }
11715  }
11716  Pos = Remainder.Pos(';'); // 4th delimiter
11717  AnsiString MaxRunningSpeedStr = Remainder.SubString(1, Pos - 1);
11718 
11719  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11720  if(MaxRunningSpeedStr == "")
11721  {
11722  TimetableMessage(GiveMessages, "Train maximum running speed missing in '" + TrainInfoStr + "'");
11723  Utilities->CallLogPop(892);
11724  return(false);
11725  }
11726  for(int x = 1; x < MaxRunningSpeedStr.Length() + 1; x++)
11727  {
11728  if((MaxRunningSpeedStr[x] < '0') || (MaxRunningSpeedStr[x] > '9'))
11729  {
11730  TimetableMessage(GiveMessages, "Train maximum running speed contains invalid characters in '" + TrainInfoStr + "'");
11731  Utilities->CallLogPop(893);
11732  return(false);
11733  }
11734  }
11735  MaxRunningSpeed = MaxRunningSpeedStr.ToInt();
11736  if(MaxRunningSpeed > TTrain::MaximumSpeedLimit) // 400kph = 250mph
11737  {
11738  MaxRunningSpeed = TTrain::MaximumSpeedLimit;
11739  if(!MRSHigh) // added at v2.4.0
11740  {
11741  TimetableMessage(GiveMessages, "Train maximum running speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11742  MRSHigh = true;
11743  }
11744  }
11745  if(MaxRunningSpeed < 10)
11746  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11747  {
11748  MaxRunningSpeed = 10;
11749  if(!MRSLow) // added at v2.4.0
11750  {
11751  TimetableMessage(GiveMessages, "Train maximum running speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11752  MRSLow = true;
11753  }
11754  }
11755  Pos = Remainder.Pos(';'); // 5th delimiter
11756  AnsiString MassStr = Remainder.SubString(1, Pos - 1);
11757 
11758  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11759  if(MassStr == "")
11760  {
11761  TimetableMessage(GiveMessages, "Train mass missing in '" + TrainInfoStr + "'");
11762  Utilities->CallLogPop(895);
11763  return(false);
11764  }
11765  for(int x = 1; x < MassStr.Length() + 1; x++)
11766  {
11767  if((MassStr[x] < '0') || (MassStr[x] > '9'))
11768  {
11769  TimetableMessage(GiveMessages, "Train mass contains invalid characters in '" + TrainInfoStr + "'");
11770  Utilities->CallLogPop(896);
11771  return(false);
11772  }
11773  }
11774  Mass = MassStr.ToInt() * 1000; // convert tonnes to kg
11775  if(Mass > TTrain::MaximumMassLimit) // 10,000tonnes
11776  {
11777  Mass = TTrain::MaximumMassLimit;
11778  if(!MassHigh) // added at v2.4.0
11779  {
11780  TimetableMessage(GiveMessages, "Train mass > 10,000 tonnes in '" + TrainInfoStr + "'. Setting it to 10,000 tonnes");
11781  MassHigh = true;
11782  }
11783  }
11784  if(Mass == 0)
11785  {
11786  TimetableMessage(GiveMessages, "Train mass zero in '" + TrainInfoStr + "'");
11787  Utilities->CallLogPop(897);
11788  return(false);
11789  }
11790  Pos = Remainder.Pos(';'); // 6th delimiter
11791  AnsiString MaxBrakeForceStr = Remainder.SubString(1, Pos - 1);
11792 
11793  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11794  if(MaxBrakeForceStr == "")
11795  {
11796  TimetableMessage(GiveMessages, "Train braking force missing in '" + TrainInfoStr + "'");
11797  Utilities->CallLogPop(898);
11798  return(false);
11799  }
11800  for(int x = 1; x < (MaxBrakeForceStr.Length() + 1); x++)
11801  {
11802  if((MaxBrakeForceStr[x] != '.') && ((MaxBrakeForceStr[x] < '0') || (MaxBrakeForceStr[x] > '9')))
11803  {
11804  TimetableMessage(GiveMessages, "Train braking force contains invalid characters in '" + TrainInfoStr + "'");
11805  Utilities->CallLogPop(899);
11806  return(false);
11807  }
11808  }
11809  double MaxBrakeForce = MaxBrakeForceStr.ToDouble() * 1000;
11810 
11811  // convert to kg force
11812  if((MaxBrakeForce / Mass) > 1) // gives 'g' braking - 9.81m/s/s
11813  {
11814  MaxBrakeForce = Mass;
11815  if(!BFHigh) // added at v2.4.0
11816  {
11817  TimetableMessage(GiveMessages, "Train braking force too high in '" + TrainInfoStr + "'. Setting it to the same as the train mass");
11818  BFHigh = true;
11819  }
11820  }
11821  if((MaxBrakeForce / Mass) < 0.01)
11822  {
11823  MaxBrakeForce = Mass * 0.01;
11824  if(!BFLow) // added at v2.4.0
11825  {
11826  TimetableMessage(GiveMessages, "Train braking force too low in '" + TrainInfoStr + "'. Setting it to 1% of the train mass");
11827  BFLow = true;
11828  }
11829  }
11830  // convert to m/s/s
11831  MaxBrakeRate = MaxBrakeForce / Mass * 9.81;
11832  // now may have just a power entry or power and signaller max. speed
11833  AnsiString GrossPowerStr = "", SignallerSpeedStr = "";
11834 
11835  if(SemiColonCount == 6)
11836  {
11837  GrossPowerStr = Remainder;
11838  SignallerSpeedStr = "30"; // default value
11839  }
11840  else // must be 7
11841  {
11842  Pos = Remainder.Pos(';'); // 7th delimiter
11843  GrossPowerStr = Remainder.SubString(1, Pos - 1);
11844  SignallerSpeedStr = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11845  }
11846  // deal with GrossPower
11847  if(GrossPowerStr == "")
11848  {
11849  TimetableMessage(GiveMessages, "Train power missing in '" + TrainInfoStr + "'");
11850  Utilities->CallLogPop(901);
11851  return(false);
11852  }
11853  for(int x = 1; x < GrossPowerStr.Length() + 1; x++)
11854  {
11855  if((GrossPowerStr[x] < '0') || (GrossPowerStr[x] > '9'))
11856  {
11857  TimetableMessage(GiveMessages, "Train power contains invalid characters in '" + TrainInfoStr + "'");
11858  Utilities->CallLogPop(902);
11859  return(false);
11860  }
11861  }
11862 
11863  double GrossPower = GrossPowerStr.ToInt() * 1000; // convert to W
11864 
11865  if(GrossPower > TTrain::MaximumPowerLimit) // 100MW
11866  {
11867  GrossPower = TTrain::MaximumPowerLimit;
11868  if(!PwrHigh)
11869  {
11870  TimetableMessage(GiveMessages, "Train power > 100,000kW in '" + TrainInfoStr + "'. Setting it to 100,000kW");
11871  PwrHigh = true;
11872  }
11873  }
11874  else if(GrossPower == 0) // changed at v2.4.0
11875  {
11876  GrossPower = 0.1;
11877  // can't be zero or AValue is zero and then have divide by zero error, so set to 0.1W so acceleration tiny (though should be intercepted before accel calculated)
11878  }
11879  else if((GrossPower > 0) && (GrossPower < 10000))
11880  // added at v2.4.0 to ensure min power of 8kW at rail unless zero (otherwise could have too low AValues
11881  {
11882  GrossPower = 10000;
11883  }
11884  PowerAtRail = GrossPower * 0.8;
11885  // apply ratio of 80% for rail to gross power (seems about average from an internet search)
11886 
11887  // deal with SignallerSpeed
11888  if(SignallerSpeedStr == "")
11889  {
11890  TimetableMessage(GiveMessages, "Signaller speed not set in '" + TrainInfoStr + "', either set a value or remove the extra semicolon");
11891  Utilities->CallLogPop(1771);
11892  return(false);
11893  }
11894  for(int x = 1; x < SignallerSpeedStr.Length() + 1; x++)
11895  {
11896  if((SignallerSpeedStr[x] < '0') || (SignallerSpeedStr[x] > '9'))
11897  {
11898  TimetableMessage(GiveMessages, "Signaller speed contains invalid characters in '" + TrainInfoStr + "'");
11899  Utilities->CallLogPop(1769);
11900  return(false);
11901  }
11902  }
11903  SignallerSpeed = SignallerSpeedStr.ToInt();
11904  if(SignallerSpeed > TTrain::MaximumSpeedLimit)
11905  {
11906  SignallerSpeed = TTrain::MaximumSpeedLimit;
11907  if(!SigSHigh)
11908  {
11909  TimetableMessage(GiveMessages, "Signaller speed > 400km/h in '" + TrainInfoStr + "'. Setting it to 400km/h");
11910  SigSHigh = true;
11911  }
11912  }
11913  if(SignallerSpeed < 10)
11914  // changed at v0.6 to prevent low max speeds - can cause problems in SetTrainMovementValues
11915  {
11916  SignallerSpeed = 10;
11917  if(!SigSLow)
11918  {
11919  TimetableMessage(GiveMessages, "Signaller speed can't be less than 10km/h in '" + TrainInfoStr + "', it will be set to 10km/h");
11920  SigSLow = true;
11921  }
11922  // Utilities->CallLogPop(1770);
11923  // return false;
11924  }
11925  Utilities->CallLogPop(904);
11926  return(true);
11927 }
11928 
11929 // ---------------------------------------------------------------------------
11930 
11931 bool TTrainController::SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &NumberOfRepeats,
11932  bool GiveMessages)
11933 {
11934  // Format must be: R;mm;dd;nn mm may be 1, 2 or more digits, dd may be 1 or 2 digits, nn may be 1, 2 or more digits
11935  // function checks validity of each item and returns false for error
11936  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SplitRepeat," + OneEntry);
11937  if(OneEntry.Length() < 7)
11938  {
11939  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11940  Utilities->CallLogPop(865);
11941  return(false);
11942  }
11943  int SemiColonCount = 0;
11944 
11945  for(int x = 1; x < OneEntry.Length() + 1; x++)
11946  {
11947  if(OneEntry[x] == ';')
11948  {
11949  SemiColonCount++;
11950  }
11951  }
11952  if(SemiColonCount != 3)
11953  {
11954  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11955  Utilities->CallLogPop(866);
11956  return(false);
11957  }
11958  if((OneEntry[1] != 'R') || (OneEntry[2] != ';'))
11959  {
11960  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - should be 'R;m;d;n'");
11961  Utilities->CallLogPop(867);
11962  return(false);
11963  }
11964  AnsiString Remainder = OneEntry.SubString(3, OneEntry.Length() - 2);
11965  // strip off R;
11966 
11967  int Pos = 0;
11968 
11969  Pos = Remainder.Pos(';');
11970  AnsiString MinutesStr = Remainder.SubString(1, Pos - 1);
11971 
11972  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
11973  if(MinutesStr == "")
11974  {
11975  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute increment segment missing");
11976  Utilities->CallLogPop(868);
11977  return(false);
11978  }
11979  if(MinutesStr.Length() > 3)
11980  // added for v2.3.1 following Albie Vowles' reported error in repeat value 03/02/20
11981  {
11982  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - minute value too high, maximum value is 999");
11983  Utilities->CallLogPop(2119);
11984  return(false);
11985  }
11986  for(int x = 1; x < MinutesStr.Length() + 1; x++)
11987  {
11988  if((MinutesStr[x] < '0') || (MinutesStr[x] > '9'))
11989  {
11990  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in minute increment segment");
11991  Utilities->CallLogPop(869);
11992  return(false);
11993  }
11994  }
11995  RearStartOrRepeatMins = MinutesStr.ToInt();
11996  if(RearStartOrRepeatMins == 0)
11997  {
11998  TimetableMessage(GiveMessages, "Repeat minute increment is zero in: '" + OneEntry + "' - can't have a zero value");
11999  Utilities->CallLogPop(870);
12000  return(false);
12001  }
12002  Pos = Remainder.Pos(';');
12003  AnsiString DigitsStr = Remainder.SubString(1, Pos - 1);
12004 
12005  Remainder = Remainder.SubString(Pos + 1, Remainder.Length() - Pos);
12006  if(DigitsStr == "")
12007  {
12008  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - headcode increment segment missing");
12009  Utilities->CallLogPop(871);
12010  return(false);
12011  }
12012  for(int x = 1; x < DigitsStr.Length() + 1; x++)
12013  {
12014  if((DigitsStr[x] < '0') || (DigitsStr[x] > '9'))
12015  {
12016  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in headcode increment segment");
12017  Utilities->CallLogPop(872);
12018  return(false);
12019  }
12020  }
12021  if(DigitsStr.Length() > 2)
12022  {
12023  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - maximum number of digits for headcode increment is 2");
12024  Utilities->CallLogPop(873);
12025  return(false);
12026  }
12027  FrontStartOrRepeatDigits = DigitsStr.ToInt();
12028 /* allow zero digit increments so HC can stay same for repeated services - for many suburban services the headcode digits relate to the
12029  route rather than the service
12030  if(FrontStartOrRepeatDigits == 0)
12031  {
12032  TimetableMessage(GiveMessages, "Repeat headcode increment is zero in: '" + OneEntry + "' - can't have a zero value");
12033  Utilities->CallLogPop(874);
12034  return false;
12035  }
12036 */
12037  if(!Last2CharactersBothDigits(0, ServiceReference) && (FrontStartOrRepeatDigits > 0))
12038  // new for v0.6b for unrestricted headcodes
12039  {
12040  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry +
12041  "' - a repeating service with incrementing digits must have digits as its last two headcode characters");
12042  Utilities->CallLogPop(1889);
12043  return(false);
12044  }
12045  AnsiString NumberStr = Remainder;
12046 
12047  if(NumberStr == "")
12048  {
12049  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - number of repeats missing");
12050  Utilities->CallLogPop(875);
12051  return(false);
12052  }
12053  if(NumberStr.Length() > 4)
12054  // added for v2.3.1 following Albie Vowles' reported error 03/02/20
12055  {
12056  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - repeat value too high, no timetabled event can exceed 95 hours & 59 minutes");
12057  Utilities->CallLogPop(2118);
12058  return(false);
12059  }
12060  for(int x = 1; x < NumberStr.Length() + 1; x++)
12061  {
12062  if((NumberStr[x] < '0') || (NumberStr[x] > '9'))
12063  // catches negative numbers
12064  {
12065  TimetableMessage(GiveMessages, "Error in repeat: '" + OneEntry + "' - non-digit character in number of repeats");
12066  Utilities->CallLogPop(876);
12067  return(false);
12068  }
12069  }
12070  NumberOfRepeats = NumberStr.ToInt();
12071  if(NumberOfRepeats == 0)
12072  {
12073  TimetableMessage(GiveMessages, "Number of repeats is zero in: '" + OneEntry + "' - if no repeats are needed the repeat should be omitted");
12074  Utilities->CallLogPop(877);
12075  return(false);
12076  }
12077  Utilities->CallLogPop(878);
12078  return(true);
12079 }
12080 
12081 // ---------------------------------------------------------------------------
12082 
12083 bool TTrainController::SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag) //TwoLocationFlag added at v2.9.1
12084 /* Note that here the TrainDataVector has been compiled with FinalCall true in ProcessOneTimetableLine so work on the
12085  vector rather than the timetable
12086  Note also that for unlocated Snt entries the LocationType hasn't yet been set
12087 
12088  Many of the errors caught here duplicate those in the preliminary checks, but leave in for completeness
12089 
12090  For info:-
12091  class TActionVectorEntry //contains a single train action - repeat entry is also of this class though no train action is taken for it
12092  {
12093  public:
12094  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode; ///< string values for timetabled event entries, null
12096  bool SignallerControl; ///< indicates a train that is defined by the timetable as under signaller control
12097  bool Warning; ///< if set triggers an alert in the warning panel when the action is reached
12098  int NumberOfRepeats; ///< the number of repeating services
12099  int RearStartOrRepeatMins, FrontStartOrRepeatDigits; ///< dual-purpose variables used for the TrackVectorPositions of the rear and front
12101  TDateTime EventTime, ArrivalTime, DepartureTime; ///< relevant times at which the action is timetabled, zeroed on creation so change
12103  TNumList ExitList; ///< the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
12104  TTimetableFormatType FormatType; ///< defines the timetable action type
12105  TTimetableLocationType LocationType; ///< indicates where the train is when the relevant action occurs
12106  TTimetableSequenceType SequenceType; ///< indicates where in the sequence of codes the action lies
12107  TTimetableShuttleLinkType ShuttleLinkType; ///< indicates whether or not the action relates to a shuttle service link
12108  TTrainDataEntry *LinkedTrainEntryPtr; ///< link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle
12110  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr; ///< pointer used by shuttles for the non-shuttle train links, in & out, the
12112 
12113  // inline function
12114 
12116  TActionVectorEntry() {
12117  RearStartOrRepeatMins=0; FrontStartOrRepeatDigits=0; NumberOfRepeats=0; FormatType=NoFormat;
12118  SequenceType=NoSequence; LocationType=NoLocation; ShuttleLinkType=NoShuttleLink, EventTime=TDateTime(-1);
12119  ArrivalTime=TDateTime(-1); DepartureTime=TDateTime(-1); LinkedTrainEntryPtr=0; NonRepeatingShuttleLinkEntryPtr=0;
12120  Warning = false; SignallerControl = false;
12121  }
12122  };
12123 
12124  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
12125 
12126  class TTrainDataEntry //contains all data for a single train - copied into train object when becomes active
12127  {
12128  public:
12129  AnsiString HeadCode, ServiceReference, Description; ///< headcode is the first train's headcode, rest are calculated from repeat
12132  double MaxBrakeRate; ///< in metres/sec/sec
12133  double MaxRunningSpeed; ///< in km/h
12134  double PowerAtRail; ///< in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
12135  int Mass; ///< in kg
12136  int NumberOfTrains; ///< number of repeats + 1
12137  int SignallerSpeed; ///< in km/h for use when under signaller control
12138  int StartSpeed; ///< in km/h
12139  TActionVector ActionVector; ///< all the actions for the train
12140  TTrainOperatingDataVector TrainOperatingDataVector; ///< operating information for the train including all its repeats
12141 
12142  //inline function
12143 
12145  TTrainDataEntry()
12146  {
12147  StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;
12148  }
12149  };
12150 
12151  Allowable successors:-
12152  Snt unlocated -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas; No others
12153  Snt located -> No starts, no finishes except Frh & Fjo (as of v2.0.0), no repeat, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12154  Snt-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK
12155  Sfs -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12156  set location, else fails)
12157  Sns -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12158  set location, else fails)
12159  Sns-sh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12160  set location, else fails)
12161  Sns-fsh -> No starts, finishes, repeats, pas or TimeTimeLoc; any other cmd or TimeLoc (dep) OK (must have a TimeLoc departure somewhere in sequence to
12162  set location, else fails)
12163  Fns -> R only
12164  F-nshs -> Nothing (no repeats permitted)
12165  Fjo -> R only
12166  Frh -> R only
12167  Fer -> R only
12168  Frh-sh -> R only
12169  Fns-sh -> R only
12170  jbo -> No starts, finishes, repeats, splits, pas or TimeTimeLoc; TimeLoc (dep), jbo or cdt OK
12171  fsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12172  rsp -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12173  cdt -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12174  TimeLoc (arr) -> No starts, repeats, Fer, pas or TimeTimeLoc; TimeLoc (dep) or any other OK
12175  TimeLoc (dep) -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12176  TimeTimeLoc -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12177  (new) pas -> Fer, TimeLoc (arr), or TimeTimeLoc, (new) pas OK, no others
12178  Repeat -> Nothing
12179 
12180  There must be a TimeLoc arrival (or a Sns start at location) in a sequence so successive cmd locations can be set
12181  Check all Snt's & set Locations if located (located = zero start speed, either element at a location (but if rear element
12182  is a continuation then treated as unlocated), and location listed in the next TimeLoc entry, though needn't be immediately after)
12183  If Snt entry at a location specified in a following TimeLoc entry but start speed > 0 give error message
12184  Check all times increase or stay same through ActionVector
12185  Cycle through all entries in vector setting arr & dep times based on above list
12186  Add locations to all relevant cmd entries based on earlier arrival location (or earlier reference for Sfs & Sns)
12187  Check locations match the arr & dep TimeLoc entries
12188  Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs
12189  Make above valid succession checks
12190  Check all splits have matching Sfs headcodes (both ways), add locations to SFSs & check times same
12191  Check all new service headcodes (Sns) have matching headcodes (both ways), add locations to SNHs & check times same
12192  Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
12193  Check each Fns has matching Sns headcodes (both ways), add locations to SNHs & check times same
12194  Check all joins have matching headcodes (both ways), locations & times & don't occur in same sequence
12195  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
12196  Set train info for Sfs & Sns entries
12197  Check each repeat entry exactly matches any included joins or splits (user has to enter it to show that really wants it)
12198  Check at least one platform long enough for a split (only need 2 lengths) & disallow if not, need length of 2 & 1 extra
12199  element at each end, or length of 3 & 1 extra element at either end
12200  Check all TimeLocs have either Arr or Dep times set and EventTime == -1
12201  Check all Cmds have EventTime set & Arr & Dep times = -1
12202  Check all Sfs & Sns entries followed somewhere in sequence by a TimeLoc departure
12203  Check all locations except unlocated Snts, Fers and Repeats have a LocationName
12204 
12205  Give messages in function if errors detected and clear the vector. Return false for failure.
12206 */
12207 
12208 /* Earlier checks:-
12209  Checks carried out with error messages in this function:-
12210  At least one comma in the line (it's based on a csv file);
12211  No entries following train information;
12212  At least one comma in remainder after train information (i.e at least a start and a finish entry);
12213  SplitEntry returns false in an intermediate entry - message repeats the entry for information;
12214  First entry not a start entry;
12215  Train information incomplete before a start entry;
12216  Entry follows a finish entry but doesn't begin with 'R';
12217  SplitEntry returns false in a finish entry - message repeats the entry for information;
12218  Last action entry isn't a finish entry.
12219 
12220  Function returns false with no message if:-
12221  Timetable start time invalid (no message is given for an invalid time as the line is assumed to be an irrelevant line; if no start
12222  time is found at all then an error message is given in the calling function);
12223  SplitTrainInfo returns false (message given in called function);
12224  SplitRepeat returns false (message given in called function).
12225 
12226 Double crosslink (shuttle) table: [OtherHeadCode, NonRepeatingShuttleLinkHeadCode, LinkedTrainEntryPtr, NonRepeatingShuttleLinkEntryPtr] <-- these for easier searching for this table
12227 
12228 Command Format OtherHead NonRepeating- LinkedTrain- NonRepeating- Decsription
12229  Code ShuttleLink- EntryPtr ShuttleLink
12230  HeadCode EntryPtr
12231 
12232 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
12233 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
12234 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
12235 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
12236 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
12237 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
12238 
12239 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
12240 
12241 */{
12242  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SecondPassActions,");
12243  if(TrainDataVector.empty())
12244  {
12245  SecondPassMessage(GiveMessages, "Error in timetable - there appear to be no train services in the timetable, it must contain at least one");
12246  TrainDataVector.clear();
12247  Utilities->CallLogPop(1832);
12248  return(false);
12249  }
12250 /* new preliminary checks for v0.2b without changing anything, carry each out separately:-
12251  1) must have at least one actionvector entry
12252  2) if first actionvector entry not SignallerControl then must have at least one more actionvector entry
12253  3) if first actionvector entry is SignallerControl then must have no more actionvector entries except a repeat
12254  4) first entry must be a start;
12255  4a) if first entry is Snt and not signallercontrol and second is a finish then it must be at a location with zero start speed
12256  5) a start must be the first entry;
12257  6) a repeat entry must be the last;
12258  7) for other than SignallerControl the last entry must be repeat or finish; if last entry is a repeat the last but one must be a finish;
12259  8) a finish entry must be the last or last but one, and if last but one the last must be a repeat
12260  Other successor errors will be caught later as all 'throws' changed to messages prior to the bulk of the sucessor checks
12261 */
12262 
12263  TwoLocationList.clear(); //empty the list to begin with, added at v2.9.1
12264  TwoLocationFlag = false; //added at v2.9.1
12265  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (1)
12266  {
12267  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12268  if(TrainDataVector.at(x).ActionVector.empty())
12269  {
12270  SecondPassMessage(GiveMessages, "Error in timetable - the following service has no listed events, there must be at least one: " + TDEntry.HeadCode);
12271  TrainDataVector.clear();
12272  Utilities->CallLogPop(1833);
12273  return(false);
12274  }
12275  }
12276  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (2)
12277  {
12278  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12279  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12280  if(!(AVEntry0.SignallerControl))
12281  {
12282  if(TrainDataVector.at(x).ActionVector.size() == 1)
12283  {
12284  SecondPassMessage(GiveMessages, "Error in timetable - service must have a start event and at least one other for: " + TDEntry.HeadCode);
12285  TrainDataVector.clear();
12286  Utilities->CallLogPop(1822);
12287  return(false);
12288  }
12289  }
12290  }
12291  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (3)
12292  {
12293  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12294  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12295  if(AVEntry0.SignallerControl)
12296  {
12297  if(TrainDataVector.at(x).ActionVector.size() > 2)
12298  {
12299  SecondPassMessage(GiveMessages,
12300  "Error in timetable - a signaller control service can have no more than one item (a repeat) after the start event, see: " +
12301  TDEntry.HeadCode);
12302  TrainDataVector.clear();
12303  Utilities->CallLogPop(1837);
12304  return(false);
12305  }
12306  if(TrainDataVector.at(x).ActionVector.size() > 1)
12307  {
12308  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12309  if(AVEntry1.FormatType != Repeat)
12310  {
12311  SecondPassMessage(GiveMessages,
12312  "Error in timetable - a signaller control service cannot have any other than a repeat after the start event, see: " + TDEntry.HeadCode);
12313  TrainDataVector.clear();
12314  Utilities->CallLogPop(1838);
12315  return(false);
12316  }
12317  }
12318  }
12319  }
12320  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (4)
12321  {
12322  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12323  TActionVectorEntry AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12324  if(AVEntry0.SequenceType != Start)
12325  {
12326  SecondPassMessage(GiveMessages, "Error in timetable - the first event must be a start for: " + TDEntry.HeadCode);
12327  TrainDataVector.clear();
12328  Utilities->CallLogPop(1824);
12329  return(false);
12330  }
12331  if((AVEntry0.Command == "Snt") && !(AVEntry0.SignallerControl))
12332  // 4a added at v2.0.0. This is only a rough check, Fer only valid for an unlocated Snt
12333  // and others for a located Snt, but those checks done later
12334  {
12335  TActionVectorEntry AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12336  // must be a second entry if first not signallercontrol
12337  if((AVEntry1.SequenceType == Finish) && ((AVEntry1.Command == "Fns-sh") || (AVEntry1.Command == "Frh-sh")))
12338  {
12339  SecondPassMessage(GiveMessages, "Error in timetable - finish events Fns-sh and Frh-sh not permitted immediately after an Snt entry for: " +
12340  TDEntry.HeadCode);
12341  TrainDataVector.clear();
12342  Utilities->CallLogPop(2046);
12343  return(false);
12344  }
12345  }
12346  }
12347  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (5)
12348  {
12349  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12350  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12351  {
12352  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12353  if((AVEntry.SequenceType == Start) && (y != 0))
12354  {
12355  SecondPassMessage(GiveMessages, "Error in timetable - a start event is present that is not the first event for: " + TDEntry.HeadCode);
12356  TrainDataVector.clear();
12357  Utilities->CallLogPop(1825);
12358  return(false);
12359  }
12360  }
12361  }
12362  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (6)
12363  {
12364  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12365  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12366  {
12367  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12368  if((AVEntry.FormatType == Repeat) && (y != (TrainDataVector.at(x).ActionVector.size() - 1)))
12369  {
12370  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is present that is not the last item for: " + TDEntry.HeadCode);
12371  TrainDataVector.clear();
12372  Utilities->CallLogPop(1826);
12373  return(false);
12374  }
12375  }
12376  }
12377  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (7)
12378  {
12379  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12380  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12381  {
12382  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12383  if((y == 0) && AVEntry.SignallerControl)
12384  {
12385  break;
12386  }
12387  if(y == (TrainDataVector.at(x).ActionVector.size() - 1))
12388  {
12389  if((AVEntry.FormatType != Repeat) && (AVEntry.SequenceType != Finish))
12390  {
12391  SecondPassMessage(GiveMessages, "Error in timetable - the last item must be either a finish event or a repeat for: " + TDEntry.HeadCode);
12392  TrainDataVector.clear();
12393  Utilities->CallLogPop(1827);
12394  return(false);
12395  }
12396  if(AVEntry.FormatType == Repeat)
12397  {
12398  const TActionVectorEntry &LastAVEntry = TrainDataVector.at(x).ActionVector.at(y - 1);
12399  if(LastAVEntry.SequenceType != Finish)
12400  {
12401  SecondPassMessage(GiveMessages, "Error in timetable - the last event before the repeat must be a finish for: " + TDEntry.HeadCode);
12402  TrainDataVector.clear();
12403  Utilities->CallLogPop(1828);
12404  return(false);
12405  }
12406  }
12407  }
12408  }
12409  }
12410  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // (8)
12411  {
12412  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12413  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12414  {
12415  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12416  if(AVEntry.SequenceType == Finish)
12417  {
12418  if((y != (TrainDataVector.at(x).ActionVector.size() - 1)) && (y != (TrainDataVector.at(x).ActionVector.size() - 2)))
12419  {
12420  SecondPassMessage(GiveMessages, "Error in timetable - a finish event must be either the last or last but one for: " + TDEntry.HeadCode);
12421  TrainDataVector.clear();
12422  Utilities->CallLogPop(1829);
12423  return(false);
12424  }
12425  if(y == (TrainDataVector.at(x).ActionVector.size() - 2))
12426  {
12427  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12428  {
12429  SecondPassMessage(GiveMessages, "Error in timetable - the only event that can follow a finish event is a repeat for: " +
12430  TDEntry.HeadCode);
12431  TrainDataVector.clear();
12432  Utilities->CallLogPop(1830);
12433  return(false);
12434  }
12435  }
12436  }
12437  }
12438  }
12439 
12440  // end of new preliminary checks
12441 
12442  // check ActionVector present and check start event successor validity
12443  // For Snt & Snt-sh set location if stopped, don't set any times yet
12444  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12445  {
12446  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12447  TActionVectorEntry & AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12448  // use reference so can change internals where necessary
12449  if((AVEntry0.Command == "Snt") || (AVEntry0.Command == "Snt-sh"))
12450  {
12451  AnsiString LocationName = "";
12452  if(IsSNTEntryLocated(0, TDEntry, LocationName))
12453  // it is at a location
12454  {
12455  if(TDEntry.StartSpeed == 0) // stopped
12456  {
12457  AVEntry0.LocationName = LocationName;
12458  AVEntry0.LocationType = AtLocation;
12459  // check successor validity for located Snt that isn't a SignallerControl entry
12460  if(!AVEntry0.SignallerControl)
12461  {
12462  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12463  // at least 2 entries present checked in integrity check so (1) valid
12464  if(!AtLocSuccessor(AVEntry1))
12465  {
12466  // Frh following Snt-sh will return false in location check, so no need to check here
12467  SecondPassMessage(GiveMessages, "Error in timetable - stopped 'Snt' or 'Snt-sh' followed by an illegal event for: " +
12468  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12469  TrainDataVector.clear();
12470  Utilities->CallLogPop(523);
12471  return(false);
12472  }
12473  }
12474  }
12475  else
12476  {
12477  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt' or 'Snt-sh' event at stop location but start speed not zero for: " +
12478  TDEntry.HeadCode);
12479  TrainDataVector.clear();
12480  Utilities->CallLogPop(791);
12481  return(false);
12482  }
12483  }
12484  else // check not Snt-sh & carry out successor validity checks for unlocated Snt that isn't a SignallerControl entry
12485  {
12486  if(AVEntry0.Command == "Snt-sh")
12487  {
12488  SecondPassMessage(GiveMessages, "Error in timetable - 'Snt-sh' event not at stop location for: " + TDEntry.HeadCode);
12489  TrainDataVector.clear();
12490  Utilities->CallLogPop(1042);
12491  return(false);
12492  }
12493  AVEntry0.LocationType = EnRoute;
12494  if(!AVEntry0.SignallerControl)
12495  {
12496  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12497  // at least 2 entries checked in integrity check so (1) valid
12498  if(!MovingSuccessor(AVEntry1))
12499  {
12500  SecondPassMessage(GiveMessages, "Error in timetable - unlocated 'Snt' not followed by 'Fer', 'pas' or an arrival for: " +
12501  TDEntry.HeadCode);
12502  TrainDataVector.clear();
12503  Utilities->CallLogPop(790);
12504  return(false);
12505  }
12506  }
12507  }
12508  }
12509  // check other start successors
12510  else if(AVEntry0.SequenceType == Start)
12511  {
12512  const TActionVectorEntry &AVEntry1 = TrainDataVector.at(x).ActionVector.at(1);
12513  // at least 2 entries present checked in integrity check so (1) valid
12514  if(!AtLocSuccessor(AVEntry1))
12515  {
12516  SecondPassMessage(GiveMessages, "Error in timetable - 'Sfs', 'Sns', 'Sns-sh' or 'Sns-fsh' followed by an illegal event for: " +
12517  TDEntry.HeadCode + ". The event isn't valid for a stationary train.");
12518  TrainDataVector.clear();
12519  Utilities->CallLogPop(793);
12520  return(false);
12521  }
12522  }
12523  }
12524 
12525  // set Sfs, Sns, Sns-sh & 'Sns-fsh' locations same as following TimeLoc departure entry location, if no departure before end of sequence give error message
12526  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12527  {
12528  bool FoundFlag = false;
12529  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12530  TActionVectorEntry & AVEntry = TrainDataVector.at(x).ActionVector.at(0);
12531  // use reference so can change internals
12532  if((AVEntry.Command == "Sfs") || (AVEntry.Command == "Sns") || (AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Sns-fsh"))
12533  {
12534  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size(); y++)
12535  {
12536  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y);
12537  if(AVEntry2.FormatType == TimeLoc)
12538  {
12539  FoundFlag = true;
12540  AVEntry.LocationName = AVEntry2.LocationName;
12541  break;
12542  }
12543  }
12544  if(!FoundFlag)
12545  {
12546  SecondPassMessage(GiveMessages, "Error in timetable - no location departure following an 'Sfs', 'Sns', 'Sns-sh'or 'Sns-fsh' event for: " +
12547  TDEntry.HeadCode);
12548  TrainDataVector.clear();
12549  Utilities->CallLogPop(851);
12550  return(false);
12551  }
12552  }
12553  }
12554 
12555  // set all cmd locations based on earlier location name in TimeLoc arrival or Sfs/Sns/Sns-sh/Sns-fsh/located Snt/Snt-sh locations
12556  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12557  {
12558  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12559  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12560  {
12561  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12562  if((AVEntry.FormatType == TimeLoc) || ((AVEntry.SequenceType == Start) && (AVEntry.LocationType == AtLocation)))
12563  {
12564  if(AVEntry.LocationName == "")
12565  // if TimeLoc turns out to be a TimeLoc departure then will emerge & be rejected in successor checks for TimeLocs
12566  {
12567  SecondPassMessage(GiveMessages, "Error in timetable for " + TDEntry.HeadCode +
12568  ": an event should have had a location name associated with it but it could not be found");
12569  TrainDataVector.clear();
12570  Utilities->CallLogPop(1831);
12571  return(false);
12572  // throw Exception("Error, entry location null in TimeLoc/Sfs/Sns/Sns-sh/Sns-fsh/Snt-sh/located Snt for Train: " + TDEntry.HeadCode);
12573  }
12574  for(unsigned int z = y + 1; z < TrainDataVector.at(x).ActionVector.size(); z++)
12575  {
12576  TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(z);
12577  // use reference so can change internals where necessary
12578  if((AVEntry2.Command != "") && (AVEntry2.LocationType == AtLocation))
12579  {
12580  AVEntry2.LocationName = AVEntry.LocationName;
12581  }
12582  else
12583  {
12584  break;
12585  }
12586  }
12587  }
12588  }
12589  }
12590  // all location names now set
12591 
12592  // check remaining successor validity except for TimeLoc arr & dep since those times not set yet
12593  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12594  {
12595  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12596  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12597  {
12598  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12599  if((AVEntry.SequenceType == Finish) && (AVEntry.Command != "F-nshs"))
12600  {
12601  if(y < (TrainDataVector.at(x).ActionVector.size() - 1))
12602  // i.e at least one more, must be a repeat
12603  {
12604  if(TrainDataVector.at(x).ActionVector.at(y + 1).FormatType != Repeat)
12605  {
12606  SecondPassMessage(GiveMessages, "Error in timetable - only a repeat can follow a finish entry for: " + TDEntry.HeadCode);
12607  TrainDataVector.clear();
12608  Utilities->CallLogPop(798);
12609  return(false);
12610  }
12611  }
12612  }
12613  if(AVEntry.Command == "F-nshs")
12614  {
12615  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12616  // i.e has to be the last
12617  {
12618  SecondPassMessage(GiveMessages, "Error in timetable - F-nshs (shuttle link) must be the last event for: " + TDEntry.HeadCode);
12619  TrainDataVector.clear();
12620  Utilities->CallLogPop(1049);
12621  return(false);
12622  }
12623  }
12624  if(AVEntry.Command == "pas")
12625  {
12626  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12627  {
12628  SecondPassMessage(GiveMessages, "Error in timetable - a 'pas' can't be the last event for: " + TDEntry.HeadCode);
12629  TrainDataVector.clear();
12630  Utilities->CallLogPop(1518);
12631  return(false);
12632  }
12633  }
12634  if(AVEntry.Command == "jbo")
12635  {
12636  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12637  {
12638  SecondPassMessage(GiveMessages, "Error in timetable - a 'jbo' can't be the last event for: " + TDEntry.HeadCode);
12639  TrainDataVector.clear();
12640  Utilities->CallLogPop(800);
12641  return(false);
12642  }
12643  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12644  if(!AtLocSuccessor(AVEntry2))
12645  {
12646  SecondPassMessage(GiveMessages, "Error in timetable - a jbo event is followed by an illegal event for: " + TDEntry.HeadCode +
12647  ". The event isn't valid for a stationary train.");
12648  TrainDataVector.clear();
12649  Utilities->CallLogPop(801);
12650  return(false);
12651  }
12652  }
12653  if((AVEntry.Command == "fsp") || (AVEntry.Command == "rsp"))
12654  {
12655  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12656  {
12657  SecondPassMessage(GiveMessages, "Error in timetable - a train split can't be the last event for: " + TDEntry.HeadCode);
12658  TrainDataVector.clear();
12659  Utilities->CallLogPop(802);
12660  return(false);
12661  }
12662  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12663  if(!AtLocSuccessor(AVEntry2))
12664  {
12665  SecondPassMessage(GiveMessages, "Error in timetable - a train split is followed by an illegal event for: " + TDEntry.HeadCode +
12666  ". The event isn't valid for a stationary train.");
12667  TrainDataVector.clear();
12668  Utilities->CallLogPop(803);
12669  return(false);
12670  }
12671  }
12672  if(AVEntry.Command == "cdt")
12673  {
12674  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12675  {
12676  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' can't be the last event for: " + TDEntry.HeadCode);
12677  TrainDataVector.clear();
12678  Utilities->CallLogPop(804);
12679  return(false);
12680  }
12681  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12682  if(!AtLocSuccessor(AVEntry2))
12683  {
12684  SecondPassMessage(GiveMessages, "Error in timetable - a 'cdt' is followed by an illegal event for: " + TDEntry.HeadCode +
12685  ". The event isn't valid for a stationary train.");
12686  TrainDataVector.clear();
12687  Utilities->CallLogPop(805);
12688  return(false);
12689  }
12690  }
12691  if(AVEntry.FormatType == TimeTimeLoc)
12692  {
12693  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12694  {
12695  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure can't be the last event for: " + TDEntry.HeadCode);
12696  TrainDataVector.clear();
12697  Utilities->CallLogPop(806);
12698  return(false);
12699  }
12700  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12701  if(!MovingSuccessor(AVEntry2))
12702  {
12703  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure is followed by an illegal event for: " +
12704  TDEntry.HeadCode + ". The event isn't valid for a moving train.");
12705  TrainDataVector.clear();
12706  Utilities->CallLogPop(807);
12707  return(false);
12708  }
12709  }
12710  if(AVEntry.FormatType == PassTime)
12711  {
12712  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12713  {
12714  SecondPassMessage(GiveMessages, "Error in timetable - a pass time can't be the last event for: " + TDEntry.HeadCode);
12715  TrainDataVector.clear();
12716  Utilities->CallLogPop(1530);
12717  return(false);
12718  }
12719  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12720  if(!MovingSuccessor(AVEntry2))
12721  {
12722  SecondPassMessage(GiveMessages, "Error in timetable - a pass time is followed by an illegal event for: " + TDEntry.HeadCode +
12723  ". The event isn't valid for a moving train.");
12724  TrainDataVector.clear();
12725  Utilities->CallLogPop(1531);
12726  return(false);
12727  }
12728  }
12729  if(AVEntry.FormatType == Repeat)
12730  {
12731  if(y != (TrainDataVector.at(x).ActionVector.size() - 1))
12732  {
12733  SecondPassMessage(GiveMessages, "Error in timetable - a repeat is not the last item for: " + TDEntry.HeadCode);
12734  TrainDataVector.clear();
12735  Utilities->CallLogPop(808);
12736  return(false);
12737  }
12738  }
12739  }
12740  }
12741 
12742  // set arrival & departure times for TimeLocs & set their EventTimes to -1
12743  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12744  {
12745  bool LastEntryIsAnArrival = false;
12746  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
12747  // first deal with unlocated Snt entries - so next entry (TimeLoc or TimeTimeLoc) is an arrival, all else stopped so the next TimeLoc is a departure
12748  const TActionVectorEntry &AVEntry0 = TrainDataVector.at(x).ActionVector.at(0);
12749  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
12750  // StartSpeed may or may not be 0, but train will move forwards (if capable of doing so), & next TimeLoc will be an arrival, whether or not after one or more TimeTimeLocs
12751  {
12752  LastEntryIsAnArrival = false;
12753  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12754  {
12755  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12756  if(AVEntry.FormatType == TimeLoc)
12757  {
12758  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12759  {
12760  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12761  }
12762  if(LastEntryIsAnArrival)
12763  {
12764  AVEntry.DepartureTime = AVEntry.EventTime;
12765  AVEntry.EventTime = TDateTime(-1);
12766  LastEntryIsAnArrival = false;
12767  }
12768  else // last entry a departure
12769  {
12770  AVEntry.ArrivalTime = AVEntry.EventTime;
12771  AVEntry.EventTime = TDateTime(-1);
12772  LastEntryIsAnArrival = true;
12773  }
12774  }
12775  }
12776  }
12777  else // all others stopped at beginning
12778  {
12779  LastEntryIsAnArrival = true;
12780  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12781  {
12782  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12783  if(AVEntry.FormatType == TimeLoc)
12784  {
12785  if((AVEntry.ArrivalTime > TDateTime(-1)) || (AVEntry.DepartureTime > TDateTime(-1)) || (AVEntry.EventTime == TDateTime(-1)))
12786  {
12787  throw Exception("Timetable error, TimeLoc times not as initially set for " + TDEntry.HeadCode);
12788  }
12789  if(LastEntryIsAnArrival)
12790  {
12791  AVEntry.DepartureTime = AVEntry.EventTime;
12792  AVEntry.EventTime = TDateTime(-1);
12793  LastEntryIsAnArrival = false;
12794  }
12795  else // last entry a departure
12796  {
12797  AVEntry.ArrivalTime = AVEntry.EventTime;
12798  AVEntry.EventTime = TDateTime(-1);
12799  LastEntryIsAnArrival = true;
12800  }
12801  }
12802  }
12803  }
12804  }
12805  // perform remaining successor checks for TimeLocs
12806  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12807  {
12808  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12809  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12810  {
12811  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12812  if((AVEntry.FormatType == TimeLoc) && (AVEntry.ArrivalTime >= TDateTime(0))) // arrival
12813  // TimeLoc (arr) -> No starts, repeats, Fer or TimeTimeLoc; TimeLoc (dep) or any other OK
12814  {
12815  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12816  {
12817  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival can't be the last event for: " + TDEntry.HeadCode);
12818  TrainDataVector.clear();
12819  Utilities->CallLogPop(809);
12820  return(false);
12821  }
12822  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12823  if(!AtLocSuccessor(AVEntry2))
12824  {
12825  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival is followed by an illegal event for: " + TDEntry.HeadCode +
12826  ". The event isn't valid for a stationary train.");
12827  TrainDataVector.clear();
12828  Utilities->CallLogPop(810);
12829  return(false);
12830  }
12831  }
12832  if((AVEntry.FormatType == TimeLoc) && (AVEntry.DepartureTime >= TDateTime(0))) // departure
12833  // TimeLoc (dep) -> Fer, TimeLoc (arr), TimeTimeLoc, (new) pas OK, no others
12834  {
12835  if(y >= (TrainDataVector.at(x).ActionVector.size() - 1))
12836  {
12837  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure can't be the last event for: " + TDEntry.HeadCode);
12838  TrainDataVector.clear();
12839  Utilities->CallLogPop(811);
12840  return(false);
12841  }
12842  const TActionVectorEntry &AVEntry2 = TrainDataVector.at(x).ActionVector.at(y + 1);
12843  if(!MovingSuccessor(AVEntry2))
12844  {
12845  SecondPassMessage(GiveMessages, "Error in timetable - a timed departure is followed by an illegal event for: " + TDEntry.HeadCode +
12846  ". The event isn't valid for a moving train.");
12847  TrainDataVector.clear();
12848  Utilities->CallLogPop(812);
12849  return(false);
12850  }
12851  }
12852  }
12853  }
12854 
12855  // check all TimeLocs have either Arr or Dep time set and EventTime == -1, all Cmds have EventTime set & Arr & Dep times == -1,
12856  // & repeats have no times set
12857  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12858  {
12859  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12860  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12861  {
12862  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12863  if(AVEntry.FormatType == TimeLoc)
12864  {
12865  if(AVEntry.EventTime != TDateTime(-1))
12866  {
12867  throw Exception("Timetable error, TimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12868  }
12869  if((AVEntry.ArrivalTime == TDateTime(-1)) && (AVEntry.DepartureTime == TDateTime(-1)))
12870  {
12871  throw Exception("Timetable error, TimeLoc entry has neither arrival nor departure time set for " + TDEntry.HeadCode);
12872  }
12873  }
12874  if(AVEntry.FormatType == TimeTimeLoc)
12875  {
12876  if(AVEntry.EventTime != TDateTime(-1))
12877  {
12878  throw Exception("Timetable error, TimeTimeLoc entry has EventTime not -1 for " + TDEntry.HeadCode);
12879  }
12880  if((AVEntry.ArrivalTime == TDateTime(-1)) || (AVEntry.DepartureTime == TDateTime(-1)))
12881  {
12882  throw Exception("Timetable error, TimeTimeLoc entry has either arrival or departure time not set for " + TDEntry.HeadCode);
12883  }
12884  }
12885  if((AVEntry.FormatType == TimeCmd) || (AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == StartNew) ||
12886  (AVEntry.FormatType == SNTShuttle) || (AVEntry.FormatType == SNSShuttle) || (AVEntry.FormatType == FNSNonRepeatToShuttle) ||
12887  (AVEntry.FormatType == FSHNewService) || (AVEntry.FormatType == PassTime))
12888  {
12889  if(AVEntry.EventTime == TDateTime(-1))
12890  {
12891  throw Exception("Timetable error, Cmd or PassTime entry has EventTime not set for " + TDEntry.HeadCode);
12892  }
12893  if((AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12894  {
12895  throw Exception("Timetable error, Cmd or PassTime entry has either arrival or departure time set for " + TDEntry.HeadCode);
12896  }
12897  }
12898  if(AVEntry.FormatType == Repeat)
12899  {
12900  if((AVEntry.EventTime != TDateTime(-1)) || (AVEntry.ArrivalTime != TDateTime(-1)) || (AVEntry.DepartureTime != TDateTime(-1)))
12901  {
12902  throw Exception("Timetable error, Repeat entry has a time set for " + TDEntry.HeadCode);
12903  }
12904  }
12905  }
12906  }
12907 
12908  // check times stay same or increase, note that can have time of 0 if include midnight
12909  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12910  {
12911  TDateTime CurrentTime = TTClockTime; // the timetable start time
12912  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12913  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
12914  {
12915  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
12916  if(AVEntry.FormatType == Repeat)
12917  {
12918  break;
12919  }
12920  if(AVEntry.FormatType == FinRemHere)
12921  {
12922  break;
12923  }
12924  if(AVEntry.FormatType == TimeTimeLoc)
12925  {
12926  if(AVEntry.DepartureTime < AVEntry.ArrivalTime)
12927  {
12928  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has a later arrival than departure time for: " +
12929  TDEntry.HeadCode);
12930  TrainDataVector.clear();
12931  Utilities->CallLogPop(813);
12932  return(false);
12933  }
12934  if(AVEntry.ArrivalTime < CurrentTime)
12935  {
12936  SecondPassMessage(GiveMessages, "Error in timetable - a timed arrival and departure has too early an arrival time for: " +
12937  TDEntry.HeadCode);
12938  TrainDataVector.clear();
12939  Utilities->CallLogPop(814);
12940  return(false);
12941  }
12942  CurrentTime = AVEntry.DepartureTime;
12943  continue;
12944  }
12945  if(AVEntry.FormatType == TimeLoc)
12946  {
12947  if(AVEntry.ArrivalTime >= TDateTime(0))
12948  {
12949  if(AVEntry.ArrivalTime < CurrentTime)
12950  {
12951  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12952  TrainDataVector.clear();
12953  Utilities->CallLogPop(815);
12954  return(false);
12955  }
12956  CurrentTime = AVEntry.ArrivalTime;
12957  }
12958  else
12959  {
12960  if(AVEntry.DepartureTime < CurrentTime)
12961  // both may be 0 legitimately so must allow for this
12962  {
12963  SecondPassMessage(GiveMessages, "Error in timetable - a timed location event has a time that is too early for: " + TDEntry.HeadCode);
12964  TrainDataVector.clear();
12965  Utilities->CallLogPop(816);
12966  return(false);
12967  }
12968  CurrentTime = AVEntry.DepartureTime;
12969  }
12970  continue;
12971  }
12972  if(AVEntry.EventTime < CurrentTime)
12973  // all others have EventTime set
12974  {
12975  SecondPassMessage(GiveMessages, "Error in timetable - a train event has a time that is set too early for: " + TDEntry.HeadCode +
12976  ", may be before timetable start time");
12977  TrainDataVector.clear();
12978  Utilities->CallLogPop(835);
12979  return(false);
12980  }
12981  CurrentTime = AVEntry.EventTime;
12982  continue;
12983  }
12984  }
12985 
12986  // check locations consistent
12987  AnsiString LastLocationName = "";
12988 
12989  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
12990  {
12991  bool LastEntryIsAnArrival = false;
12992  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
12993  // first deal with moving Snt entries (all else stopped)
12994  if((TrainDataVector.at(x).ActionVector.at(0).Command == "Snt") && (TrainDataVector.at(x).ActionVector.at(0).LocationType == EnRoute))
12995  {
12996  LastEntryIsAnArrival = false;
12997  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName; // should be ""
12998  if(LastLocationName != "")
12999  {
13000  throw Exception("Timetable error, moving Snt entry has LocationName set for " + TDEntry.HeadCode);
13001  }
13002  for(unsigned int y = 1; y < TrainDataVector.at(x).ActionVector.size();
13003  y++) // note that immediate successor to a moving Snt can only be a Moving type
13004  {
13005  // if it's a SignallerControl entry then the condition isn't met
13006  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13007  if(AVEntry.FormatType == Repeat)
13008  {
13009  break; // repeat = reached end (+allows repeat after signaller controlled entry)
13010  }
13011  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13012  {
13013  if(AVEntry.LocationName != LastLocationName)
13014  {
13015  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13016  AVEntry.Command);
13017  TrainDataVector.clear();
13018  Utilities->CallLogPop(823);
13019  return(false);
13020  }
13021  }
13022  else if(AVEntry.FormatType == TimeCmd)
13023  // cdt is the only TimeCmd
13024  {
13025  if(AVEntry.LocationName != LastLocationName)
13026  {
13027  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13028  AVEntry.Command);
13029  TrainDataVector.clear();
13030  Utilities->CallLogPop(824);
13031  return(false);
13032  }
13033  }
13034  else if(AVEntry.FormatType == TimeTimeLoc)
13035  {
13036  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13037  // last entry must be a departure or would have failed earlier
13038  {
13039  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13040  TwoLocationFlag = true;
13041 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13042 // TwoOrMoreLocationsWarningGiven = true;
13043  }
13044  LastLocationName = AVEntry.LocationName;
13045  LastEntryIsAnArrival = false;
13046  }
13047  else if(AVEntry.FormatType == TimeLoc)
13048  {
13049  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13050  {
13051  SecondPassMessage(GiveMessages,
13052  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13053  TrainDataVector.clear();
13054  Utilities->CallLogPop(826);
13055  return(false);
13056  }
13057  else if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName))
13058  {
13059  SecondPassMessage(GiveMessages,
13060  "Error in timetable - a location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode);
13061  TrainDataVector.clear();
13062  Utilities->CallLogPop(827);
13063  return(false);
13064  }
13065  LastLocationName = AVEntry.LocationName;
13066  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13067  }
13068  }
13069  }
13070  else // all stationary starting entries
13071  {
13072  LastEntryIsAnArrival = true;
13073  LastLocationName = TrainDataVector.at(x).ActionVector.at(0).LocationName;
13074  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13075  {
13076  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13077  if(AVEntry.FormatType == Repeat)
13078  {
13079  break;
13080  }
13081  else if((AVEntry.FormatType == TimeCmdHeadCode) || (AVEntry.FormatType == FNSNonRepeatToShuttle))
13082  // no need to add anything for shuttle starts since they are at loc (0) anyway
13083  {
13084  if(AVEntry.LocationName != LastLocationName)
13085  {
13086  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13087  AVEntry.Command);
13088  TrainDataVector.clear();
13089  Utilities->CallLogPop(828);
13090  return(false);
13091  }
13092  }
13093  else if(AVEntry.FormatType == TimeCmd)
13094  // cdt is the only TimeCmd
13095  {
13096  if(AVEntry.LocationName != LastLocationName)
13097  {
13098  SecondPassMessage(GiveMessages, "Error in timetable - a location event is inconsistent for: " + TDEntry.HeadCode + " && " +
13099  AVEntry.Command);
13100  TrainDataVector.clear();
13101  Utilities->CallLogPop(829);
13102  return(false);
13103  }
13104  }
13105  else if(AVEntry.FormatType == TimeTimeLoc)
13106  {
13107  if((AVEntry.LocationName == LastLocationName) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13108  // last entry must be a departure or would have failed earlier
13109  {
13110  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13111  TwoLocationFlag = true;
13112 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13113 // TwoOrMoreLocationsWarningGiven = true;
13114  }
13115  LastLocationName = AVEntry.LocationName;
13116  LastEntryIsAnArrival = false;
13117  }
13118  else if(AVEntry.FormatType == TimeLoc)
13119  {
13120  if(LastEntryIsAnArrival && (AVEntry.LocationName != LastLocationName))
13121  {
13122  SecondPassMessage(GiveMessages,
13123  "Error in timetable - a location event for a timed departure is different from the arrival location for: " + TDEntry.HeadCode);
13124  TrainDataVector.clear();
13125  Utilities->CallLogPop(831);
13126  return(false);
13127  }
13128  if(!LastEntryIsAnArrival && (AVEntry.LocationName == LastLocationName) && !TwoOrMoreLocationsWarningGiven)
13129  {
13130  SecondPassMessage(GiveMessages,
13131  "A location event for a timed arrival is the same as the earlier departure location for: " + TDEntry.HeadCode + ". Please correct if this is an error.\n\nThis warning will not be shown again.");
13133 // TrainDataVector.clear();
13134 // Utilities->CallLogPop(832);
13135 // return false;
13136  }
13137  LastLocationName = AVEntry.LocationName;
13138  LastEntryIsAnArrival = !LastEntryIsAnArrival;
13139  }
13140  }
13141  }
13142  }
13143 
13144  // Check same location doesn't appear twice before a cdt except for separate arr & dep TimeLocs (just a potential error warning given in v2.6.0)
13145  // i.e. same location can appear in any number of consecutive entries but once changed couldn't repeat before a direction change prior to v2.6.0
13146  AnsiString LocationNameToBeChecked = "";
13147 
13148  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13149  {
13150  const TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13151  unsigned int y = 0;
13152  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
13153  // first discard unlocated Snt entries as they don't have location name set
13154  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
13155  {
13156  y = 1;
13157  }
13158  while(y < TDEntry.ActionVector.size())
13159  // need to check each location name separately in turn, skipped for SignallerControl entries
13160  {
13161  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat))
13162  {
13163  break; // out of the 'while' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13164  }
13165  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
13166  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
13167  {
13168  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
13169  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat))
13170  {
13171  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
13172  }
13173  if(AVEntry.Command == "cdt")
13174  {
13175  break; // out of the 'z' loop since the check is only valid up to a change of direction
13176  }
13177  if(AVEntry.LocationName == LocationNameToBeChecked)
13178  {
13179  continue; // keep going while name same
13180  }
13181  if(AVEntry.LocationName != LocationNameToBeChecked)
13182  // if name different check forwards to see if repeats
13183  {
13184  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
13185  {
13186  if(TDEntry.ActionVector.at(a).Command == "cdt")
13187  {
13188  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
13189  }
13190  if((TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked) && TTEditPanelVisible) //changed at v2.6.0 to allow loops & consecutive same locs
13191  {
13192  TwoLocationList.insert(TwoLocationList.end(), TDEntry.ServiceReference); //added at v2.9.1
13193  TwoLocationFlag = true;
13194 // ShowMessage("Two or more locations are the same without a change of direction between them. Please correct if this is an error.\n\nThis warning will not be shown again.");
13195 // TwoOrMoreLocationsWarningGiven = true;
13196  }
13197  }
13198  break; // out of the 'z' loop since have checked 'a' as far as need to
13199  }
13200  }
13201  y++;
13202  }
13203  }
13204  if(TwoLocationFlag)
13205  {
13206  TwoLocationList.sort(); //need to sort first in alphabetical order to ensure all duplictes removed
13207  TwoLocationList.unique(); //remove duplicates
13208  }
13209 
13210  // check all locations except unlocated 'Snt' & 'Fer' have LocationName set
13211  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13212  {
13213  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13214  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13215  {
13216  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13217  if((AVEntry.LocationName == "") && (AVEntry.Command != "Snt") && (AVEntry.Command != "Fer") && (AVEntry.FormatType != Repeat))
13218  {
13219  throw Exception("Error, non- 'Snt', 'Fer' or Repeat entry doesn't have a location name set for " + TDEntry.HeadCode);
13220  }
13221  AnsiString LocName = "";
13222  // dummy, only used so can call IsSNTEntryLocated
13223  if((AVEntry.Command == "Snt") && (IsSNTEntryLocated(1, TrainDataVector.at(x), LocName)))
13224  {
13225  if(AVEntry.LocationName == "")
13226  {
13227  throw Exception("Error, 'Snt' entry at a stop location doesn't have a location name set for " + TDEntry.HeadCode);
13228  }
13229  }
13230  if((AVEntry.Command == "Snt") && !(IsSNTEntryLocated(2, TrainDataVector.at(x), LocName)))
13231  {
13232  if(AVEntry.LocationName != "")
13233  {
13234  throw Exception("Error, 'Snt' unlocated entry has a location name set for " + TDEntry.HeadCode);
13235  }
13236  }
13237  }
13238  }
13239 
13240 /* Check a split to 'x' doesn't again split to 'x' (anywhere, not just for one train, since headcodes can be duplicated)
13241  Check each joined by train not joined by same train again (anywhere, not just for one train, since headcodes can be duplicated)
13242  Check each change of headcode not repeated anywhere else (anywhere, not just for one train, since headcodes can be duplicated)
13243 
13244  i.e. check everywhere where there is an 'OtherHeadCode' that it matches once only with its reference (both ways) + set
13245  the OtherHeadCodeStartingEntryPtr pointers where appropriate + train information for splits & new services
13246 
13247  BUT need to separate the shuttles from non-shuttles, because can have two trains reference each other in both forms,
13248  eg 2F44 Sns-sh ends in Fns to 2F45, & Sns 2F45 ends in Fns-sh to 2F44. Here 2F45 is the 'OtherHeadCode' for both
13249  Sns-sh & Fns in train 2F44, & 2F44 is the 'OtherHeadCode' for both Sns & Fns-sh in train 2F45.
13250 */
13251  for(unsigned int x = 0; x < TrainDataVector.size(); x++) // new test to ensure no duplicate links at all, other checks ensure none for shuttles,
13252  {
13253  // non-shuttles & non-repeating links separately, but don't check that there isn't a
13254  // duplicate between a non-repeating shuttle and another - leave original tests in as
13255  // these also set the pointers
13256  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13257  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13258  {
13259  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13260  if(AVEntry.OtherHeadCode != "")
13261  {
13262  if(!CheckForDuplicateCrossReferences(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, GiveMessages))
13263  {
13264  Utilities->CallLogPop(1584);
13265  return(false); // error message given in called function
13266  }
13267  }
13268  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13269  {
13270  if(!CheckForDuplicateCrossReferences(1, TDEntry.HeadCode, AVEntry.NonRepeatingShuttleLinkHeadCode, GiveMessages))
13271  {
13272  Utilities->CallLogPop(1585);
13273  return(false); // error message given in called function
13274  }
13275  }
13276  }
13277  }
13278 
13279  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13280  {
13281  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13282  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13283  {
13284  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13285  if((AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13286  {
13287  if(AVEntry.OtherHeadCode != "")
13288  {
13289  if(!CheckCrossReferencesAndSetData(0, TDEntry.HeadCode, AVEntry.OtherHeadCode, false, GiveMessages))
13290  // false = non-shuttle
13291  {
13292  Utilities->CallLogPop(864);
13293  return(false); // error message given in called function
13294  }
13295  }
13296  }
13297  }
13298  }
13299 
13300  // now repeat the check just for the shuttles
13301  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13302  {
13303  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13304  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13305  {
13306  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13307  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh"))
13308  {
13309  if(AVEntry.OtherHeadCode != "")
13310  {
13311  if(!CheckCrossReferencesAndSetData(1, TDEntry.HeadCode, AVEntry.OtherHeadCode, true, GiveMessages))
13312  // true = shuttle
13313  {
13314  Utilities->CallLogPop(1100);
13315  return(false); // error message given in called function
13316  }
13317  }
13318  }
13319  }
13320  }
13321 
13322  // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
13323  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13324  {
13325  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13326  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13327  {
13328  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13329  if(AVEntry.NonRepeatingShuttleLinkHeadCode != "")
13330  {
13332  {
13333  Utilities->CallLogPop(1060);
13334  return(false); // error message given in called function
13335  }
13336  }
13337  }
13338  }
13339 
13340  // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
13341  // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
13342  // don't ever need to and as designed would skip repeats
13343  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13344  {
13345  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13346  {
13347  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13348  if((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh"))
13349  {
13350  if(!CheckShuttleServiceIntegrity(0, &(TrainDataVector.at(x)), GiveMessages))
13351  {
13352  Utilities->CallLogPop(1090);
13353  return(false); // error message given in called function
13354  }
13355  }
13356  }
13357  }
13358 
13359  // check all entries have all types set to something
13360  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13361  {
13362  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13363  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13364  {
13365  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13366  if(AVEntry.FormatType == NoFormat)
13367  {
13368  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has FormatType unset for: " + TDEntry.HeadCode);
13369  }
13370  else if(AVEntry.SequenceType == NoSequence)
13371  {
13372  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has SequenceType unset for: " + TDEntry.HeadCode);
13373  }
13374  else if(AVEntry.LocationType == NoLocation)
13375  {
13376  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has LocationType unset for: " + TDEntry.HeadCode);
13377  }
13378  else if(AVEntry.ShuttleLinkType == NoShuttleLink)
13379  {
13380  throw Exception("Error - timetable ActionVector entry no. " + AnsiString(y) + " has ShuttleLinkType unset for: " + TDEntry.HeadCode);
13381  }
13382  }
13383  }
13384 
13385  // all OK if reach here, so set up the TrainOperatingDataVector (already has one entry) & NumberOfTrains
13386  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13387  {
13388  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
13389  // non-const reference so can alter content
13390  TTrainOperatingData TData;
13391  const TActionVectorEntry &LastAVEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
13392  if(LastAVEntry.FormatType == Repeat) // check if a repeat
13393  {
13394 /*
13395  class TTrainOperatingData
13396  {
13397  public:
13398  int TrainID; - default, set at construction
13399  TActionEventType EventReported; used during operation
13400  TRunningEntry RunningEntry; - default, set at construction
13401  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;} //constructor, values set to defaults
13402  };
13403 */
13404  TDEntry.NumberOfTrains = LastAVEntry.NumberOfRepeats + 1;
13405  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
13406  {
13407  TDEntry.TrainOperatingDataVector.push_back(TData);
13408  }
13409  }
13410  else
13411  {
13412  TDEntry.NumberOfTrains = 1;
13413  }
13414  }
13415 
13416  // check that don't include any Continuation names
13417  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13418  {
13419  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13420  {
13421  AnsiString LocName = TrainDataVector.at(x).ActionVector.at(y).LocationName;
13422  AnsiString HC = TrainDataVector.at(x).HeadCode;
13423  if(LocName != "")
13424  {
13425  if(Track->ContinuationNameMap.find(LocName) != Track->ContinuationNameMap.end())
13426  {
13427  SecondPassMessage(GiveMessages, "Error in timetable - continuation names (" + LocName + ") must not be included, see service " + HC);
13428  TrainDataVector.clear();
13429  Utilities->CallLogPop(1578);
13430  return(false);
13431  }
13432  }
13433  }
13434  }
13435 
13436  // check that all repeat times below 96h
13437  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13438  {
13439  int NumRepeats = (TrainDataVector.at(x).NumberOfTrains) - 1;
13440  int IncMinutes = 0;
13441  if(TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).FormatType == Repeat)
13442  {
13443  IncMinutes = TrainDataVector.at(x).ActionVector.at(TrainDataVector.at(x).ActionVector.size() - 1).RearStartOrRepeatMins;
13444  }
13445  else
13446  {
13447  continue; // basic times already checked in CheckTimeValidity
13448  }
13449  AnsiString HC = TrainDataVector.at(x).HeadCode;
13450  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13451  {
13452  if((double)TrainDataVector.at(x).ActionVector.at(y).EventTime > -1)
13453  {
13454  if(((double)GetRepeatTime(32, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13455  {
13456  SecondPassMessage(GiveMessages, "Error in timetable - a repeat time exceeds 95h 59m, see service " + HC); // 3d 23h 59m = 3.9993055556
13457  TrainDataVector.clear();
13458  Utilities->CallLogPop(1818);
13459  return(false);
13460  }
13461  }
13462  if((double)TrainDataVector.at(x).ActionVector.at(y).ArrivalTime > -1)
13463  {
13464  if(((double)GetRepeatTime(33, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13465  // 3d 23h 59m = 3.9993055556
13466  {
13467  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13468  TrainDataVector.clear();
13469  Utilities->CallLogPop(1819);
13470  return(false);
13471  }
13472  }
13473  if((double)TrainDataVector.at(x).ActionVector.at(y).DepartureTime > -1)
13474  {
13475  if(((double)GetRepeatTime(34, TrainDataVector.at(x).ActionVector.at(y).EventTime, NumRepeats, IncMinutes) >= 3.9994))
13476  // 3d 23h 59m = 3.9993055556
13477  {
13478  SecondPassMessage(GiveMessages, "Error in timetable - a repeat entry time exceeds 95h 59m, see service " + HC);
13479  TrainDataVector.clear();
13480  Utilities->CallLogPop(1820);
13481  return(false);
13482  }
13483  }
13484  }
13485  }
13486 
13487  // Now that all set up change any extended headcodes back to ordinary headcodes
13488  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13489  {
13490  StripExcessFromHeadCode(0, TrainDataVector.at(x).HeadCode);
13491  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13492  {
13493  StripExcessFromHeadCode(1, TrainDataVector.at(x).ActionVector.at(y).OtherHeadCode);
13494  StripExcessFromHeadCode(2, TrainDataVector.at(x).ActionVector.at(y).NonRepeatingShuttleLinkHeadCode);
13495  }
13496  }
13497 
13498  // SaveTrainDataVectorToFile(0);//test
13500  Utilities->CallLogPop(782);
13501  return(true);
13502 }
13503 
13504 // ---------------------------------------------------------------------------
13505 // Moving successors: TimeLoc arr/TimeTimeLoc/pas/Fer;
13507 {
13508  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.FormatType == TimeTimeLoc) || (AVEntry.Command == "pas") || (AVEntry.Command == "Fer"));
13509 }
13510 
13511 // ---------------------------------------------------------------------------
13512 // AtLoc successors: TimeLoc dep/jbo/fsp/rsp/cdt/Frh/Fns/Fjo/Frh-sh/Fns-sh/F-nshs;
13514 {
13515  return ((AVEntry.FormatType == TimeLoc) || (AVEntry.Command == "jbo") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") ||
13516  (AVEntry.Command == "cdt") || (AVEntry.Command == "Frh") || (AVEntry.Command == "Fns") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh-sh") ||
13517  (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "F-nshs"));
13518 }
13519 
13520 // ---------------------------------------------------------------------------
13521 
13522 void TTrainController::StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
13523 {
13524  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripExcessFromHeadCode," + HeadCode);
13525  if(HeadCode.Length() > 4) // ignore otherwise
13526  {
13527  HeadCode = HeadCode.SubString(HeadCode.Length() - 3, 4);
13528  }
13529  Utilities->CallLogPop(1593);
13530 }
13531 
13532 // ---------------------------------------------------------------------------
13533 
13534 bool TTrainController::CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
13535 {
13536  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckForDuplicateCrossReferences," + MainHeadCode + "," +
13537  SecondHeadCode);
13538  int ForwardCount = 0;
13539  int ReverseCount = 0;
13540 
13541  if(MainHeadCode == SecondHeadCode)
13542  {
13543  SecondPassMessage(GiveMessages, "Error in timetable - Service " + MainHeadCode + " has an event that references itself");
13544  TrainDataVector.clear();
13545  Utilities->CallLogPop(1594);
13546  return(false);
13547  }
13548  // forward check
13549  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13550  {
13551  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13552  if(TDEntry.HeadCode == MainHeadCode)
13553  {
13554  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13555  {
13556  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13557  if(AVEntry.OtherHeadCode == SecondHeadCode)
13558  {
13559  ForwardCount++;
13560  }
13561  if(AVEntry.NonRepeatingShuttleLinkHeadCode == SecondHeadCode)
13562  // need own check in case both 'Other' & 'NonRepeating' have same headcode
13563  {
13564  ForwardCount++;
13565  }
13566  }
13567  }
13568  }
13569  if(ForwardCount == 0)
13570  // this is an exception because the headcodes are selected in the same order as the forward check
13571  {
13572  throw Exception("Error, ForwardCount == 0 in CheckForDuplicateCrossReferences after called with found values");
13573  }
13574  if(ForwardCount > 2)
13575  // can have 2 if one is Sns-sh linking from another leg of the shuttle, and Fns links out to that same leg
13576  {
13577  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + SecondHeadCode + " from a train whose headcode is " +
13578  MainHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13579  TrainDataVector.clear();
13580  Utilities->CallLogPop(1587);
13581  return(false);
13582  }
13583  // reverse check
13584  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13585  {
13586  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13587  if(TDEntry.HeadCode == SecondHeadCode)
13588  {
13589  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13590  {
13591  const TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13592  if(AVEntry.OtherHeadCode == MainHeadCode)
13593  {
13594  ReverseCount++;
13595  }
13596  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
13597  {
13598  ReverseCount++;
13599  }
13600  }
13601  }
13602  }
13603 
13604  if(ReverseCount == 0)
13605  {
13606  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + SecondHeadCode);
13607  TrainDataVector.clear();
13608  Utilities->CallLogPop(1588);
13609  return(false);
13610  }
13611  if(ReverseCount > 2)
13612  // can have 2 if one is a second shuttle leg with a link in from Fns, and it links out to the same service with Fxx-sh
13613  {
13614  SecondPassMessage(GiveMessages, "Error in timetable - found more than two references to " + MainHeadCode + " from a train whose headcode is " +
13615  SecondHeadCode + ". Check the service cross references from each service, and check whether one or other service is listed twice or more.");
13616  TrainDataVector.clear();
13617  Utilities->CallLogPop(1589);
13618  return(false);
13619  }
13620  if(ForwardCount != ReverseCount)
13621  {
13622  SecondPassMessage(GiveMessages, "Error in timetable - " + MainHeadCode + " has a different number of references to " + SecondHeadCode +
13623  " than the other way round");
13624  TrainDataVector.clear();
13625  Utilities->CallLogPop(1610);
13626  return(false);
13627  }
13628  Utilities->CallLogPop(1590);
13629  return(true);
13630 }
13631 
13632 // ---------------------------------------------------------------------------
13633 
13634 bool TTrainController::CheckCrossReferencesAndSetData(int Caller, AnsiString MainHeadCode, AnsiString OtherHeadCode, bool Shuttle, bool GiveMessages)
13635 /* Return false for no find or more than one find, check correct types of link
13636  First run through all trains whose headcode is the MainHeadCode (may be > 1) & for each entry whose
13637  'other' is OtherHeadCode increment a forward counter. Keep a pointer to the 'OtherHeadCode' entry for use later
13638  Must be exactly 1 forward count. NB Forward relates to MainHeadCode
13639  Then do the same in reverse.
13640  Using the pointers check the event times, then check that the locations & commands match - if main is a split then other must be Sfs;
13641  if main is Fns other must be Sns; if main is jbo other must be Fjo.
13642  Also check platform lengths OK for a split location (call to Track function for this - at least one platform at location has to be long
13643  enough). If all succeeds so far set the relevant OtherHeadCodeStartingEntryPtr to the new service starting point + train information
13644  for Sfs & Sns services. Finally check the repeat entries if present are consistent
13645 
13646  Check all except the NonRepeatingShuttleLinkHeadCodes, which only occur from F-nshs to Sns-sh, and from Fns-sh to
13647  Sns-fsh. All others should check out OK, but check shuttles & non-shuttles separately.
13648 
13649  /NB prohibit main & other headcodes being same, causes probs in failing to recognise locations
13650 */
13651 
13652 {
13653  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckCrossReferencesAndSetData," + MainHeadCode + "," + OtherHeadCode);
13654  int ForwardCount = 0;
13655  int ReverseCount = 0;
13656  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
13657  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
13658  TTrainDataEntry *MainTrainDataPtr = 0;
13659  TTrainDataEntry *OtherTrainDataPtr = 0;
13660 
13661  // forward check
13662  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13663  {
13664  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13665  if(TDEntry.HeadCode == MainHeadCode)
13666  {
13667  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13668  {
13669  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13670  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13671  {
13672  if(AVEntry.OtherHeadCode == OtherHeadCode)
13673  {
13674  MainTrainDataPtr = &TrainDataVector.at(x);
13675  ForwardEntryPtr = &AVEntry;
13676  ForwardCount++;
13677  ForwardTDVectorNumber = x;
13678  }
13679  }
13680  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") ||
13681  (AVEntry.Command == "Frh-sh")))
13682  {
13683  if(AVEntry.OtherHeadCode == OtherHeadCode)
13684  {
13685  MainTrainDataPtr = &TrainDataVector.at(x);
13686  ForwardEntryPtr = &AVEntry;
13687  ForwardCount++;
13688  ForwardTDVectorNumber = x;
13689  }
13690  }
13691  }
13692  }
13693  }
13694  if(ForwardCount == 0)
13695  // this is an exception because the headcodes are selected in the same order as the forward check
13696  {
13697  throw Exception("Error, ForwardCount == 0 in CheckCrossReferencesAndSetData after called with found values");
13698  }
13699  if(ForwardCount > 1)
13700  {
13701  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + OtherHeadCode + " from a train whose headcode is " +
13702  MainHeadCode);
13703  TrainDataVector.clear();
13704  Utilities->CallLogPop(836);
13705  return(false);
13706  }
13707  // reverse check
13708  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
13709  {
13710  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
13711  if(TDEntry.HeadCode == OtherHeadCode)
13712  {
13713  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
13714  {
13715  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
13716  if(!Shuttle && (AVEntry.Command != "Sns-sh") && (AVEntry.Command != "Snt-sh") && (AVEntry.Command != "Fns-sh") && (AVEntry.Command != "Frh-sh"))
13717  {
13718  if(AVEntry.OtherHeadCode == MainHeadCode)
13719  {
13720  OtherTrainDataPtr = &TrainDataVector.at(x);
13721  ReverseCount++;
13722  ReverseEntryPtr = &AVEntry;
13723  ReverseTDVectorNumber = x;
13724  }
13725  }
13726  else if(Shuttle && ((AVEntry.Command == "Sns-sh") || (AVEntry.Command == "Snt-sh") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh")))
13727  {
13728  if(AVEntry.OtherHeadCode == MainHeadCode)
13729  {
13730  OtherTrainDataPtr = &TrainDataVector.at(x);
13731  ReverseCount++;
13732  ReverseEntryPtr = &AVEntry;
13733  ReverseTDVectorNumber = x;
13734  }
13735  }
13736  }
13737  }
13738  }
13739 
13740  if(ReverseCount == 0)
13741  {
13742  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + OtherHeadCode);
13743  TrainDataVector.clear();
13744  Utilities->CallLogPop(837);
13745  return(false);
13746  }
13747  if(ReverseCount > 1)
13748  {
13749  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
13750  OtherHeadCode);
13751  TrainDataVector.clear();
13752  Utilities->CallLogPop(838);
13753  return(false);
13754  }
13755  // these will all be false for !Shuttle
13756  bool ForwardShuttleStart = ((ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"));
13757  bool ForwardShuttleFinish = ((ForwardEntryPtr->Command == "Fns-sh") || (ForwardEntryPtr->Command == "Frh-sh"));
13758  bool ReverseShuttleStart = ((ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"));
13759  bool ReverseShuttleFinish = ((ReverseEntryPtr->Command == "Fns-sh") || (ReverseEntryPtr->Command == "Frh-sh"));
13760 
13761  if(Shuttle && MainTrainDataPtr->ActionVector.back().FormatType != Repeat)
13762  {
13763  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat");
13764  TrainDataVector.clear();
13765  Utilities->CallLogPop(1058);
13766  return(false);
13767  }
13768  if(Shuttle && OtherTrainDataPtr->ActionVector.back().FormatType != Repeat)
13769  {
13770  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + OtherHeadCode + " does not have a repeat");
13771  TrainDataVector.clear();
13772  Utilities->CallLogPop(1059);
13773  return(false);
13774  }
13775  if(ForwardEntryPtr->LocationName == "")
13776  {
13777  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13778  ". One or other service does not have a location set");
13779  TrainDataVector.clear();
13780  Utilities->CallLogPop(526);
13781  return(false);
13782  }
13783  if(ReverseEntryPtr->LocationName == "")
13784  {
13785  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + OtherHeadCode +
13786  ". One or other service does not have a location set");
13787  TrainDataVector.clear();
13788  Utilities->CallLogPop(527);
13789  return(false);
13790  }
13791  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
13792  {
13793  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13794  " is at a different location to the referencing train " + MainHeadCode);
13795  TrainDataVector.clear();
13796  Utilities->CallLogPop(842);
13797  return(false);
13798  }
13799  // ignore shuttle repeat links for first time check
13800  if(!Shuttle)
13801  {
13802  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
13803  {
13804  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + OtherHeadCode +
13805  " has a different event time to the referencing train " + MainHeadCode);
13806  TrainDataVector.clear();
13807  Utilities->CallLogPop(525);
13808  return(false);
13809  }
13810  }
13811  // need to allow for repeat times multiplying up by repeating time for shuttle repeat links
13812  // no need to check from reverse to forward as already checked links consistent, and if include will send message twice
13813  if(ForwardShuttleStart && ReverseShuttleFinish)
13814  // Shuttle must be true if these are true
13815  {
13816  if(!CheckShuttleRepeatTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime, OtherTrainDataPtr->ActionVector.back().RearStartOrRepeatMins))
13817  {
13818  SecondPassMessage(GiveMessages, "Error in timetable - shuttle service " + MainHeadCode +
13819  " first repeat restart time not consistent with finish service " + OtherHeadCode);
13820  TrainDataVector.clear();
13821  Utilities->CallLogPop(1055);
13822  return(false);
13823  }
13824  }
13825  if((ReverseEntryPtr->Command == "Sfs") || (ReverseEntryPtr->Command == "Sns"))
13826  // doesn't matter about ForwardEntryPtr being Sfs/Sns as called for every occurrence of an 'OtherHeadCode' so won't escape
13827  {
13828  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13829  {
13830  SecondPassMessage(GiveMessages, "Error in timetable - an 'Sfs' or 'Sns' event (" + OtherHeadCode +
13831  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13832  TrainDataVector.clear();
13833  Utilities->CallLogPop(528);
13834  return(false);
13835  }
13836  }
13837  if(ReverseEntryPtr->Command == "Fjo")
13838  // doesn't matter about ForwardEntryPtr being Fjo as called for every occurrence of an 'OtherHeadCode' so won't escape
13839  {
13840  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13841  {
13842  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fjo' event (" + OtherHeadCode +
13843  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13844  TrainDataVector.clear();
13845  Utilities->CallLogPop(862);
13846  return(false);
13847  }
13848  }
13849  if(ReverseEntryPtr->Command == "Fns")
13850  // doesn't matter about ForwardEntryPtr being Fns as called for every occurrence of an 'OtherHeadCode' so won't escape
13851  {
13852  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
13853  {
13854  SecondPassMessage(GiveMessages, "Error in timetable - an 'Fns' event (" + OtherHeadCode +
13855  ") appears in the same sequence as the corresponding linked event " + MainHeadCode);
13856  TrainDataVector.clear();
13857  Utilities->CallLogPop(529);
13858  return(false);
13859  }
13860  }
13861  if(ForwardEntryPtr->Command == "Sfs")
13862  {
13863  if((ReverseEntryPtr->Command != "fsp") && (ReverseEntryPtr->Command != "rsp"))
13864  {
13865  SecondPassMessage(GiveMessages,
13866  "Error in timetable - unable to find a corresponding split train event for the train that starts from a split whose headcode is " +
13867  MainHeadCode);
13868  TrainDataVector.clear();
13869  Utilities->CallLogPop(530);
13870  return(false);
13871  }
13872  }
13873  if((ForwardEntryPtr->Command == "fsp") || (ForwardEntryPtr->Command == "rsp"))
13874  {
13875  if(ReverseEntryPtr->Command != "Sfs")
13876  {
13877  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sfs' event for the train split whose headcode is " +
13878  MainHeadCode);
13879  TrainDataVector.clear();
13880  Utilities->CallLogPop(839);
13881  return(false);
13882  }
13883  else
13884  {
13885  if(!(Track->TimetabledLocationNameAllocated(4, ForwardEntryPtr->LocationName)))
13886  {
13887  SecondPassMessage(GiveMessages, "Error in timetable - can't find timetabled location '" + ForwardEntryPtr->LocationName + "' in railway - perhaps there are concourses without platforms?");
13888  TrainDataVector.clear();
13889  Utilities->CallLogPop(849);
13890  return(false);
13891  }
13892  if(!(Track->OneNamedLocationElementAtLocation(0, ForwardEntryPtr->LocationName)))
13893  {
13894  SecondPassMessage(GiveMessages, "Error in timetable - can't find any named location elements at '" + ForwardEntryPtr->LocationName + "' - perhaps there are concourses without platforms?");
13895  TrainDataVector.clear();
13896  Utilities->CallLogPop(850);
13897  return(false);
13898  }
13899  if(!(Track->OneNamedLocationLongEnoughForSplit(0, ForwardEntryPtr->LocationName)))
13900  {
13901  SecondPassMessage(GiveMessages, "Error in timetable - location too short to split a train at " + ForwardEntryPtr->LocationName);
13902  TrainDataVector.clear();
13903  Utilities->CallLogPop(846);
13904  return(false);
13905  }
13906  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13907  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13908  if(OtherTrainDataPtr->Description == "")
13909  {
13910  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13911  }
13912  // NB: May not be set if main train is a service continuation without a description, if so can't do much about it but doesn't affect operation, just the train information display
13913  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13914  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13915  }
13916  }
13917  if(ForwardEntryPtr->Command == "Sns")
13918  {
13919  if(ReverseEntryPtr->Command != "Fns")
13920  {
13921  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fns' event for the 'Sns' train whose headcode is " +
13922  MainHeadCode + " and is formed from a service with headcode " + OtherHeadCode);
13923  TrainDataVector.clear();
13924  Utilities->CallLogPop(531);
13925  return(false);
13926  }
13927  }
13928  if(ForwardEntryPtr->Command == "Fns")
13929  {
13930  if(ReverseEntryPtr->Command != "Sns")
13931  {
13932  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns' event for the train whose headcode is " + MainHeadCode +
13933  " and forms a new service with headcode " + OtherHeadCode);
13934  TrainDataVector.clear();
13935  Utilities->CallLogPop(840);
13936  return(false);
13937  }
13938  else
13939  {
13940  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13941  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13942  if(OtherTrainDataPtr->Description == "")
13943  {
13944  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
13945  }
13946  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13947  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13948  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
13949  }
13950  }
13951  if(ForwardEntryPtr->Command == "jbo")
13952  {
13953  if(ReverseEntryPtr->Command != "Fjo")
13954  {
13955  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Fjo' event for the train whose headcode is " + MainHeadCode +
13956  " and is joined by a train with headcode " + OtherHeadCode);
13957  TrainDataVector.clear();
13958  Utilities->CallLogPop(841);
13959  return(false);
13960  }
13961  }
13962  if(ForwardEntryPtr->Command == "Fjo")
13963  {
13964  if(ReverseEntryPtr->Command != "jbo")
13965  {
13966  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'jbo' event for the train whose headcode is " + MainHeadCode +
13967  " and joins a train with headcode " + OtherHeadCode);
13968  TrainDataVector.clear();
13969  Utilities->CallLogPop(532);
13970  return(false);
13971  }
13972  else
13973  {
13974  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
13975  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
13976  if((MainTrainDataPtr->MaxRunningSpeed > 5) && (MainTrainDataPtr->MaxRunningSpeed < OtherTrainDataPtr->MaxRunningSpeed))
13977  {
13978  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
13979  }
13980  // added test for > 5 [5 used instead of 0 because of possible floating point errors - though unlikely] above at v1.3.1 because the train will have a zero MaxRunningSpeed if it continues from another service - its max speed is set when it takes over from the other service
13981  // notified of this problem by Ian Walker in his email of 25/03/13. Probably redundant anyway because the max speed is reduced at the changeover if the 'joined by' train's max speed is less.
13982  }
13983  }
13984  if(ForwardShuttleStart)
13985  // (ForwardEntryPtr->Command == "Sns-sh") || (ForwardEntryPtr->Command == "Snt-sh"))
13986  {
13987  if(!ReverseShuttleFinish)
13988  // (ReverseEntryPtr->Command != "Fns-sh") && (ReverseEntryPtr->Command != "Frh-sh"))
13989  {
13990  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + MainHeadCode +
13991  " from train whose headcode is " + OtherHeadCode + ", has to be Fns-sh, Frh-sh");
13992  TrainDataVector.clear();
13993  Utilities->CallLogPop(1056);
13994  return(false);
13995  }
13996  }
13997  if(ReverseShuttleStart)
13998  // (ReverseEntryPtr->Command == "Sns-sh") || (ReverseEntryPtr->Command == "Snt-sh"))
13999  {
14000  if(!ForwardShuttleFinish)
14001  // (ForwardEntryPtr->Command != "Fns-sh") && (ForwardEntryPtr->Command != "Frh-sh"))
14002  {
14003  SecondPassMessage(GiveMessages, "Error in timetable - incorrect shuttle link to train whose headcode is " + OtherHeadCode +
14004  " from train whose headcode is " + MainHeadCode + ", has to be Fns-sh, Frh-sh");
14005  TrainDataVector.clear();
14006  Utilities->CallLogPop(1057);
14007  return(false);
14008  }
14009  else
14010  {
14011  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14012  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14013 /* don't need LinkedTrainEntryPtr for 'OtherTrain' & don't need data transfer as this is done in the
14014  non-repeating link for Sns-sh & is provided at the outset for Snt-sh
14015 */
14016  }
14017  }
14018  // check repeat information consistent if present
14019  // note that won't be affected by the non-repeating shuttle links as these are in NonRepeatingShuttleLinkHeadCode
14020  // and those not accessed here
14021 
14022  // still need to check the non-repeating links and that they have no repeats - do that outside this function
14023  bool MainRepeat = false, OtherRepeat = false;
14024  TActionVectorEntry MainRepeatEntry, OtherRepeatEntry;
14025 
14026  if(MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14027  {
14028  MainRepeat = true;
14029  MainRepeatEntry = MainTrainDataPtr->ActionVector.at(MainTrainDataPtr->ActionVector.size() - 1);
14030  }
14031  if(OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1).FormatType == Repeat)
14032  {
14033  OtherRepeat = true;
14034  OtherRepeatEntry = OtherTrainDataPtr->ActionVector.at(OtherTrainDataPtr->ActionVector.size() - 1);
14035  }
14036  if((MainRepeat && !OtherRepeat) || (!MainRepeat && OtherRepeat))
14037  {
14038  SecondPassMessage(GiveMessages, "Error in timetable - only one repeat is provided for the train whose headcode is " + MainHeadCode +
14039  " and the associated train with headcode " + OtherHeadCode);
14040  TrainDataVector.clear();
14041  Utilities->CallLogPop(844);
14042  return(false);
14043  }
14044  if(MainRepeat && OtherRepeat)
14045  {
14046  if((MainRepeatEntry.EventTime != OtherRepeatEntry.EventTime) || (MainRepeatEntry.RearStartOrRepeatMins != OtherRepeatEntry.RearStartOrRepeatMins) ||
14047  (MainRepeatEntry.FrontStartOrRepeatDigits != OtherRepeatEntry.FrontStartOrRepeatDigits) ||
14048  (MainRepeatEntry.NumberOfRepeats != OtherRepeatEntry.NumberOfRepeats))
14049  {
14050  SecondPassMessage(GiveMessages, "Error in timetable - repeat items don't correspond for the train whose headcode is " + MainHeadCode +
14051  " and the associated train with headcode " + OtherHeadCode);
14052  TrainDataVector.clear();
14053  Utilities->CallLogPop(845);
14054  return(false);
14055  }
14056  }
14057  Utilities->CallLogPop(863);
14058  return(true);
14059 }
14060 
14061 // ---------------------------------------------------------------------------
14062 
14063 void TTrainController::StripSpaces(int Caller, AnsiString &Input)
14064 // strip both leading and trailing spaces at ends of text and spaces before and after all commas and semicolons within the text
14065 {
14066  // strip spaces from extreme ends of input
14067  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StripSpaces," + AnsiString(Input));
14068  if(Input == "")
14069  {
14070  Utilities->CallLogPop(856);
14071  return;
14072  }
14073  while(Input[1] == ' ')
14074  {
14075  if(Input.Length() > 1)
14076  {
14077  Input = Input.SubString(2, Input.Length() - 1);
14078  }
14079  else
14080  {
14081  Input = "";
14082  Utilities->CallLogPop(857);
14083  return;
14084  }
14085  }
14086  if(Input == "")
14087  {
14088  Utilities->CallLogPop(858);
14089  return;
14090  }
14091  while(Input[Input.Length()] == ' ')
14092  {
14093  if(Input.Length() > 1)
14094  {
14095  Input = Input.SubString(1, Input.Length() - 1);
14096  }
14097  else
14098  {
14099  Input = "";
14100  Utilities->CallLogPop(859);
14101  return;
14102  }
14103  }
14104  // now strip spaces immediately after all commas and semicolons within the text
14105  AnsiString Output = "";
14106  bool DelimiterFound = false;
14107 
14108  for(int x = 1; x < Input.Length() + 1; x++)
14109  {
14110  if(DelimiterFound)
14111  {
14112  if(Input[x] == ' ')
14113  {
14114  continue;
14115  }
14116  }
14117  if((Input[x] != ',') && (Input[x] != ';'))
14118  {
14119  DelimiterFound = false;
14120  Output = Output + Input[x];
14121  }
14122  else
14123  {
14124  DelimiterFound = true;
14125  Output = Output + Input[x];
14126  }
14127  }
14128  if(Output == "")
14129  {
14130  Input = "";
14131  Utilities->CallLogPop(860);
14132  return;
14133  }
14134  // now strip spaces immediately before all commas and semicolons within the text
14135  Input = Output;
14136  Output = "";
14137  DelimiterFound = false;
14138  for(int x = Input.Length(); x > 0; x--)
14139  {
14140  if(DelimiterFound)
14141  {
14142  if(Input[x] == ' ')
14143  {
14144  continue;
14145  }
14146  }
14147  if((Input[x] != ',') && (Input[x] != ';'))
14148  {
14149  DelimiterFound = false;
14150  Output = AnsiString(Input[x]) + Output;
14151  }
14152  else
14153  {
14154  DelimiterFound = true;
14155  Output = AnsiString(Input[x]) + Output;
14156  }
14157  }
14158  Input = Output;
14159  Utilities->CallLogPop(861);
14160 }
14161 
14162 // ---------------------------------------------------------------------------
14163 
14164 bool TTrainController::IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
14165 // checks if an Snt or Snt-sh entry with zero starting speed is followed (somewhere, not necessarily immediately) by a TimeLoc & has the same LocationName
14166 // and if so returns true. Also returns true for Snt, not Snt-sh, if at least 1 start element is a location & the entry is either
14167 // a signaller control entry & speed is zero or it is followed immediately by Frh or Fjo (mod at v2.0.0 for empty stock pickup).
14168 // Always return false for entry at a continuation (may be named but not a stop location). Note that no successor validity checks
14169 // are done in this function, they must be done elsewhere.
14170 //a starting speed > 0 always returns false
14171 {
14172  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsSNTEntryLocated," + AnsiString(TDEntry.HeadCode));
14173  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
14174  LocationName = "";
14175  if(TDEntry.StartSpeed > 0)
14176  {
14177  Utilities->CallLogPop(1784);
14178  return(false);
14179  }
14180  if((AVEntry0.Command != "Snt") && (AVEntry0.Command != "Snt-sh"))
14181  {
14182  throw Exception("Error, first entry not 'Snt' or 'Snt-sh' in IsSNTEntryLocated");
14183  }
14185  {
14186  Utilities->CallLogPop(852);
14187  return(false);
14188  }
14189  AnsiString LocRear = Track->TrackElementAt(507, AVEntry0.RearStartOrRepeatMins).ActiveTrackElementName;
14190  AnsiString LocFront = Track->TrackElementAt(508, AVEntry0.FrontStartOrRepeatDigits).ActiveTrackElementName;
14191 
14192  if(LocRear != "")
14193  {
14194  LocationName = LocRear;
14195  }
14196  else
14197  {
14198  LocationName = LocFront;
14199  }
14200  if(LocationName == "")
14201  {
14202  Utilities->CallLogPop(1036);
14203  return(false);
14204  }
14205  if(AVEntry0.SignallerControl)
14206  {
14207  Utilities->CallLogPop(1773);
14208  return(true);
14209  }
14210 // here if not a signaller start entry so must be at least one more entry, and it is at a location
14211 
14212 //Ok Not ok continue
14213 
14214 //Frh if Snt Frh-sh cdt
14215 //Fns if Snt Fns-sh fsp or rsp
14216 //Fjo if Snt TimeTimeLoc jbo
14217 //F-nshs if Snt pas
14218 //TimeLoc dep Fer
14219 // TimeLoc arr
14220 
14221  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
14222  {
14223  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
14224  if(((AVEntry.Command == "Frh") || (AVEntry.Command == "Fjo") || (AVEntry.Command == "F-nshs") || (AVEntry.Command == "Fns")) && (AVEntry0.Command == "Snt")) // added Fjo at v2.0.0 for empty stock
14225  {
14226  Utilities->CallLogPop(1037);
14227  return(true);
14228  }
14229  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName == LocationName)) //will be a departure if same name- times not set yet so can't use them to confirm
14230  {
14231  Utilities->CallLogPop(2442);
14232  return(true);
14233  }
14234  if((AVEntry.FormatType == TimeLoc) && (AVEntry.LocationName != LocationName)) //arrival, not located
14235  {
14236  Utilities->CallLogPop(2438);
14237  return(false);
14238  }
14239  if((AVEntry.Command == "Fer") || (AVEntry.Command == "pas") || (AVEntry.Command == "Fns-sh") || (AVEntry.Command == "Frh-sh") || (AVEntry.FormatType == TimeTimeLoc))
14240  {
14241  Utilities->CallLogPop(854);
14242  return(false);
14243  }
14244  if((AVEntry.Command == "cdt") || (AVEntry.Command == "fsp") || (AVEntry.Command == "rsp") || (AVEntry.Command == "jbo"))
14245  {
14246  continue;
14247  }
14248  }
14249  Utilities->CallLogPop(855);
14250  return(false);
14251 
14252 }
14253 
14254 // ---------------------------------------------------------------------------
14255 
14256 bool TTrainController::CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
14257 {
14258  // checks that the new train start elements are valid - both exist & are connected, and that not
14259  // attempting to start on a diverging leg (i.e. one segment on points & other on element connected to diverging leg)
14260  // & not starting with front on a continuation
14261  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartPositionValidity," + RearElementStr + "," + FrontElementStr);
14262  int RearPosition = 0, FrontPosition = 0, RearExitPos = 0;
14263 
14264  RearPosition = Track->GetTrackVectorPositionFromString(5, RearElementStr, GiveMessages);
14265  if(RearPosition < 0)
14266  // error message given in GetTrackVectorPositionFromString
14267  {
14268  Utilities->CallLogPop(759);
14269  return(false);
14270  }
14271  FrontPosition = Track->GetTrackVectorPositionFromString(6, FrontElementStr, GiveMessages);
14272  if(FrontPosition < 0)
14273  // error message given in GetTrackVectorPositionFromString
14274  {
14275  Utilities->CallLogPop(760);
14276  return(false);
14277  }
14278  TTrackElement RearTrackElement = Track->TrackElementAt(490, RearPosition);
14279  TTrackElement FrontTrackElement = Track->TrackElementAt(491, FrontPosition);
14280  TTrackType RearType = RearTrackElement.TrackType, FrontType = FrontTrackElement.TrackType;
14281 
14282  // check front & rear connected
14283  for(int x = 0; x < 4; x++)
14284  {
14285  if(RearTrackElement.Conn[x] == FrontPosition)
14286  {
14287  RearExitPos = x;
14288  break;
14289  }
14290  if(x == 3)
14291  {
14292  TimetableMessage(GiveMessages, "Front element: " + FrontTrackElement.ElementID + " not linked to rear element: " + RearTrackElement.ElementID);
14293  Utilities->CallLogPop(762);
14294  return(false);
14295  }
14296  }
14297  // check not starting with front on a continuation
14298  if(FrontType == Continuation)
14299  {
14300  TimetableMessage(GiveMessages, "Front of train attempting to start on a continuation at: " + FrontElementStr);
14301  Utilities->CallLogPop(937);
14302  return(false);
14303  }
14304  // check not starting on a level crossing
14305  if(Track->IsLCAtHV(43, FrontTrackElement.HLoc, FrontTrackElement.VLoc))
14306  {
14307  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + FrontElementStr);
14308  Utilities->CallLogPop(1951);
14309  return(false);
14310  }
14311  if(Track->IsLCAtHV(44, RearTrackElement.HLoc, RearTrackElement.VLoc))
14312  {
14313  TimetableMessage(GiveMessages, "Train attempting to start on a level crossing at: " + RearElementStr);
14314  Utilities->CallLogPop(1952);
14315  return(false);
14316  }
14317  // check if trying to start on diverging leg of points
14318  if((RearType == Points) && (RearExitPos == 3))
14319  {
14320  TimetableMessage(GiveMessages, "Front of train attempting to start on element connected to diverging points at: " + RearElementStr);
14321  Utilities->CallLogPop(936);
14322  return(false);
14323  }
14324  if((FrontType == Points) && (RearTrackElement.ConnLinkPos[RearExitPos] == 3))
14325  {
14326  TimetableMessage(GiveMessages, "Rear of train attempting to start on element connected to diverging points at: " + FrontElementStr);
14327  Utilities->CallLogPop(1808);
14328  return(false);
14329  }
14330  Utilities->CallLogPop(905);
14331  return(true);
14332 }
14333 
14334 // ---------------------------------------------------------------------------
14335 
14336 bool TTrainController::CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
14337 // Rear & front element validity already checked in CheckStartPositionValidity
14338 // This checks for points in correct orientation, no train at start position and not starting on a locked route
14339 {
14340  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckStartAllowable," + AnsiString(RearPosition) + "," +
14341  AnsiString(RearExitPos));
14342  TTrackElement RearTrackElement = Track->TrackElementAt(517, RearPosition);
14343 
14344  if(RearTrackElement.TrackType == Continuation)
14345  {
14346  EventType = FailTrainEntry;
14347  }
14348  else
14349  {
14350  EventType = FailCreateTrain;
14351  }
14352  int FrontPosition = RearTrackElement.Conn[RearExitPos];
14353  TTrackElement FrontTrackElement = Track->TrackElementAt(798, FrontPosition);
14354  int FrontEntryPos = RearTrackElement.ConnLinkPos[RearExitPos];
14355  TTrackType RearType = RearTrackElement.TrackType;
14356  TTrackType FrontType = FrontTrackElement.TrackType;
14357  AnsiString RearName, FrontName;
14358 
14359  if(RearTrackElement.ActiveTrackElementName != "")
14360  {
14361  RearName = RearTrackElement.ActiveTrackElementName;
14362  }
14363  else
14364  {
14365  RearName = RearTrackElement.ElementID;
14366  }
14367  if(FrontTrackElement.ActiveTrackElementName != "")
14368  {
14369  FrontName = FrontTrackElement.ActiveTrackElementName;
14370  }
14371  else
14372  {
14373  FrontName = FrontTrackElement.ElementID;
14374  }
14375  TPrefDirElement PrefDirElement; // needed for next function but not used
14376  int LockedVectorNumber; // needed for next function but not used
14377 
14378  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(12, FrontPosition, FrontEntryPos, PrefDirElement, LockedVectorNumber))
14379  {
14380  if(ReportFlag)
14381  {
14382  if(EventType == FailCreateTrain)
14383  {
14384  EventType = FailCreateLockedRoute;
14385  }
14386  else
14387  {
14388  EventType = FailEnterLockedRoute;
14389  }
14390  LogActionError(47, HeadCode, "", EventType, FrontName);
14391  }
14392  Utilities->CallLogPop(940);
14393  return(false);
14394  }
14395  if(AllRoutes->IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(13, RearPosition, RearExitPos, PrefDirElement, LockedVectorNumber))
14396  {
14397  if(ReportFlag)
14398  {
14399  if(EventType == FailCreateTrain)
14400  {
14401  EventType = FailCreateLockedRoute;
14402  }
14403  else
14404  {
14405  EventType = FailEnterLockedRoute;
14406  }
14407  LogActionError(48, HeadCode, "", EventType, RearName);
14408  }
14409  Utilities->CallLogPop(1809);
14410  return(false);
14411  }
14412  if((RearType != Bridge) && (RearTrackElement.TrainIDOnElement > -1))
14413  {
14414  if(ReportFlag)
14415  {
14416  LogActionError(27, HeadCode, "", EventType, RearName);
14417  }
14418  Utilities->CallLogPop(1810);
14419  return(false);
14420  }
14421  if((FrontType != Bridge) && (FrontTrackElement.TrainIDOnElement > -1))
14422  {
14423  if(ReportFlag)
14424  {
14425  if(EventType == FailCreateTrain)
14426  {
14427  LogActionError(28, HeadCode, "", EventType, FrontName);
14428  }
14429  else
14430  {
14431  LogActionError(43, HeadCode, "", EventType, RearName);
14432  }
14433  }
14434  Utilities->CallLogPop(941);
14435  return(false);
14436  }
14437  if(RearType == Bridge)
14438  {
14439  if((RearExitPos > 1) && (RearTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14440  {
14441  if(ReportFlag)
14442  {
14443  LogActionError(29, HeadCode, "", EventType, RearName);
14444  }
14445  Utilities->CallLogPop(942);
14446  return(false);
14447  }
14448  if((RearExitPos < 2) && (RearTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14449  {
14450  if(ReportFlag)
14451  {
14452  LogActionError(30, HeadCode, "", EventType, RearName);
14453  }
14454  Utilities->CallLogPop(943);
14455  return(false);
14456  }
14457  }
14458  if(FrontType == Bridge)
14459  {
14460  if((FrontEntryPos > 1) && (FrontTrackElement.TrainIDOnBridgeTrackPos23 > -1))
14461  {
14462  if(ReportFlag)
14463  {
14464  if(EventType == FailCreateTrain)
14465  {
14466  LogActionError(31, HeadCode, "", EventType, FrontName);
14467  }
14468  else
14469  {
14470  LogActionError(44, HeadCode, "", EventType, RearName);
14471  }
14472  }
14473  Utilities->CallLogPop(944);
14474  return(false);
14475  }
14476  if((FrontEntryPos < 2) && (FrontTrackElement.TrainIDOnBridgeTrackPos01 > -1))
14477  {
14478  if(ReportFlag)
14479  {
14480  if(EventType == FailCreateTrain)
14481  {
14482  LogActionError(45, HeadCode, "", EventType, FrontName);
14483  }
14484  else
14485  {
14486  LogActionError(46, HeadCode, "", EventType, RearName);
14487  }
14488  }
14489  Utilities->CallLogPop(945);
14490  return(false);
14491  }
14492  }
14493  EventType = FailCreatePoints;
14494  if(RearType == Points)
14495  {
14496  if(RearTrackElement.Attribute == 1)
14497  {
14498  if(ReportFlag)
14499  {
14500  LogActionError(33, HeadCode, "", FailCreatePoints, RearName);
14501  }
14502  Utilities->CallLogPop(933);
14503  return(false);
14504  }
14505  }
14506  if(FrontType == Points)
14507  {
14508  if(FrontTrackElement.Attribute == 1)
14509  {
14510  if(ReportFlag)
14511  {
14512  LogActionError(34, HeadCode, "", FailCreatePoints, FrontName);
14513  }
14514  Utilities->CallLogPop(934);
14515  return(false);
14516  }
14517  }
14518 
14519  //this section added at v2.9.1 to prevent entry for a train when there's a route set against it
14520  int HLoc = Track->TrackElementAt(1027, RearPosition).HLoc;
14521  int VLoc = Track->TrackElementAt(1028, RearPosition).VLoc;
14522  int ELink = Track->TrackElementAt(1029, RearPosition).Link[RearExitPos]; //if route entry corresponds to RearExitPos then it's set against the train
14523  int RouteNumber; //not used
14524  if(Track->TrackElementAt(1030, RearPosition).TrackType == Continuation)
14525  {
14526  if(AllRoutes->FindRouteNumberFromRoute2MultiMapNoErrors(8, HLoc, VLoc, ELink, RouteNumber))
14527  {
14528  EventType = FailEntryRouteSetAgainst;
14529  if(ReportFlag)
14530  {
14531  LogActionError(63, HeadCode, "", EventType, RearName);
14532  }
14533  Utilities->CallLogPop(2317);
14534  return(false);
14535  }
14536  }
14537  Utilities->CallLogPop(939);
14538  return(true);
14539 }
14540 
14541 // ---------------------------------------------------------------------------
14542 
14543 AnsiString TTrainController::GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
14544 {
14545  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatHeadCode," + BaseHeadCode + "," + AnsiString(RepeatNumber) +
14546  "," + AnsiString(IncDigits));
14547  if(!Last2CharactersBothDigits(1, BaseHeadCode) && (IncDigits > 0))
14548  {
14549  throw Exception("Error, last 2 characters not both digits and IncDigits > 0 in GetRepeatHeadCode");
14550  }
14551  if(!Last2CharactersBothDigits(2, BaseHeadCode))
14552  {
14553  Utilities->CallLogPop(1893);
14554  return(BaseHeadCode);
14555  }
14556  int BaseDigits = BaseHeadCode.SubString(3, 2).ToInt();
14557  int NextRepeatDigits = BaseDigits + (IncDigits * RepeatNumber);
14558 
14559  while(NextRepeatDigits >= 100)
14560  {
14561  NextRepeatDigits -= 100; // rolls over after 99
14562  }
14563  AnsiString NextRepeatDigitsStr = AnsiString(NextRepeatDigits);
14564 
14565  if(NextRepeatDigitsStr.Length() < 2)
14566  {
14567  NextRepeatDigitsStr = AnsiString('0') + NextRepeatDigitsStr;
14568  }
14569  AnsiString NextRepeatHeadCode = BaseHeadCode.SubString(1, 2) + NextRepeatDigitsStr;
14570 
14571  Utilities->CallLogPop(1365);
14572  return(NextRepeatHeadCode);
14573 }
14574 
14575 // ---------------------------------------------------------------------------
14576 
14577 TDateTime TTrainController::GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
14578 {
14579  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetRepeatTime," + AnsiString(double(BasicTime)) + "," +
14580  AnsiString(RepeatNumber) + "," + AnsiString(IncMinutes));
14581  TDateTime NextRepeatTime = BasicTime + TDateTime(((double)(RepeatNumber * IncMinutes)) / 1440.0); // 1440 = no. of minutes in 24h
14582  Utilities->CallLogPop(1366);
14583  return(NextRepeatTime);
14584 }
14585 
14586 // ---------------------------------------------------------------------------
14587 
14588 bool TTrainController::CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
14589 // For success the ForwardEventTime + repeat time should == ReverseEventTime (allow 10secs either way since converting to doubles)
14590 {
14591  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleRepeatTime," + AnsiString(double(ForwardEventTime)) + "," +
14592  AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes));
14593  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14594  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14595  int RepeatSecs = RepeatMinutes * 60;
14596 
14597  if((ForwardSecs > (ReverseSecs - RepeatSecs + 10)) || (ForwardSecs < (ReverseSecs - RepeatSecs - 10)))
14598  {
14599  Utilities->CallLogPop(1367);
14600  return(false);
14601  }
14602  else
14603  {
14604  Utilities->CallLogPop(1368);
14605  return(true);
14606  }
14607 }
14608 
14609 // ---------------------------------------------------------------------------
14610 
14611 bool TTrainController::CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
14612 // check for proper non-repeating link cross references and that they have no repeats & that times are consistent
14613 
14614 /* Double crosslink (shuttle) table:
14615 
14616 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
14617  Code ShuttleLink- EntryPtr ShuttleLink-
14618  HeadCode EntryPtr
14619 
14620 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
14621 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
14622 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
14623 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
14624 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
14625 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
14626 
14627 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
14628 */
14629 
14630 {
14631  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinksAndSetData," + MainHeadCode + "," +
14632  NonRepeatingHeadCode);
14633  int ForwardCount = 0;
14634  int ReverseCount = 0;
14635  unsigned int ForwardTDVectorNumber, ReverseTDVectorNumber;
14636  TActionVectorEntry *ReverseEntryPtr = 0, *ForwardEntryPtr = 0;
14637  // Forward corresponds to Main, Reverse to Other
14638  TTrainDataEntry *MainTrainDataPtr = 0;
14639  TTrainDataEntry *OtherTrainDataPtr = 0;
14640 
14641  // forward check
14642  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14643  {
14644  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14645  if(TDEntry.HeadCode == MainHeadCode)
14646  {
14647  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14648  {
14649  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14650  if(AVEntry.NonRepeatingShuttleLinkHeadCode == NonRepeatingHeadCode)
14651  {
14652  MainTrainDataPtr = &TrainDataVector.at(x);
14653  ForwardEntryPtr = &AVEntry;
14654  ForwardCount++;
14655  ForwardTDVectorNumber = x;
14656  }
14657  }
14658  }
14659  }
14660  if(ForwardCount == 0)
14661  // this is an exception because the headcodes are selected in the same order as the forward check
14662  {
14663  throw Exception("Error, ForwardCount == 0 in CheckNonRepeatingShuttleLinksAndSetData after called with found values");
14664  }
14665  if(ForwardCount > 1)
14666  {
14667  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + NonRepeatingHeadCode + " from a train whose headcode is " +
14668  MainHeadCode);
14669  TrainDataVector.clear();
14670  Utilities->CallLogPop(1061);
14671  return(false);
14672  }
14673  // reverse check
14674  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
14675  {
14676  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
14677  if(TDEntry.HeadCode == NonRepeatingHeadCode)
14678  {
14679  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
14680  {
14681  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
14682  if(AVEntry.NonRepeatingShuttleLinkHeadCode == MainHeadCode)
14683  {
14684  OtherTrainDataPtr = &TrainDataVector.at(x);
14685  ReverseCount++;
14686  ReverseEntryPtr = &AVEntry;
14687  ReverseTDVectorNumber = x;
14688  }
14689  }
14690  }
14691  }
14692 
14693  if(ReverseCount == 0)
14694  {
14695  SecondPassMessage(GiveMessages, "Error in timetable - cross reference missing in either " + MainHeadCode + " or " + NonRepeatingHeadCode);
14696  TrainDataVector.clear();
14697  Utilities->CallLogPop(1062);
14698  return(false);
14699  }
14700  if(ReverseCount > 1)
14701  {
14702  SecondPassMessage(GiveMessages, "Error in timetable - found more than one reference to " + MainHeadCode + " from a train whose headcode is " +
14703  NonRepeatingHeadCode);
14704  TrainDataVector.clear();
14705  Utilities->CallLogPop(1063);
14706  return(false);
14707  }
14708  if(((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh")) && (MainTrainDataPtr->ActionVector.back().FormatType == Repeat))
14709  {
14710  SecondPassMessage(GiveMessages, "Error in timetable - shuttle connecting train " + MainHeadCode + " shouldn't have a repeat");
14711  TrainDataVector.clear();
14712  Utilities->CallLogPop(1064);
14713  return(false);
14714  }
14715  if((ForwardEntryPtr->Command != "F-nshs") && (ForwardEntryPtr->Command != "Sns-fsh") && (MainTrainDataPtr->ActionVector.back().FormatType != Repeat))
14716  {
14717  SecondPassMessage(GiveMessages, "Error in timetable - shuttle train " + MainHeadCode + " does not have a repeat item");
14718  TrainDataVector.clear();
14719  Utilities->CallLogPop(1065);
14720  return(false);
14721  }
14722  if(ForwardEntryPtr->LocationName == "")
14723  {
14724  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14725  ". One or other service does not have a location set");
14726  TrainDataVector.clear();
14727  Utilities->CallLogPop(1066);
14728  return(false);
14729  }
14730  if(ReverseEntryPtr->LocationName == "")
14731  {
14732  SecondPassMessage(GiveMessages, "Error in timetable - location error in cross referenced trains " + MainHeadCode + " and " + NonRepeatingHeadCode +
14733  ". One or other service does not have a location set");
14734  TrainDataVector.clear();
14735  Utilities->CallLogPop(1067);
14736  return(false);
14737  }
14738  if(ForwardEntryPtr->LocationName != ReverseEntryPtr->LocationName)
14739  {
14740  SecondPassMessage(GiveMessages, "Error in timetable - cross referenced train " + NonRepeatingHeadCode +
14741  " is at a different location to the referencing train " + MainHeadCode);
14742  TrainDataVector.clear();
14743  Utilities->CallLogPop(1068);
14744  return(false);
14745  }
14746  if(ForwardEntryPtr->Command == "F-nshs")
14747  // i.e. the non repeating link into the shuttle service
14748  {
14749  if(ForwardEntryPtr->EventTime != ReverseEntryPtr->EventTime)
14750  {
14751  SecondPassMessage(GiveMessages, "Error in timetable - shuttle in-link service " + MainHeadCode +
14752  " finish time not consistent with start time of shuttle service " + NonRepeatingHeadCode);
14753  TrainDataVector.clear();
14754  Utilities->CallLogPop(1069);
14755  return(false);
14756  }
14757  }
14758  if(ForwardEntryPtr->Command == "Fns-sh")
14759  // i.e. the non repeating link out from the shuttle service
14760  {
14761  if(!CheckNonRepeatingShuttleLinkTime(0, ForwardEntryPtr->EventTime, ReverseEntryPtr->EventTime,
14762  MainTrainDataPtr->ActionVector.back().RearStartOrRepeatMins, MainTrainDataPtr->ActionVector.back().NumberOfRepeats))
14763  {
14764  SecondPassMessage(GiveMessages, "Error in timetable - service " + NonRepeatingHeadCode + ", which links out from shuttle service " + MainHeadCode +
14765  ", has the wrong start time. It should correspond to the finish time of the last shuttle.");
14766  TrainDataVector.clear();
14767  Utilities->CallLogPop(1070);
14768  return(false);
14769  }
14770  }
14771  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))
14772  // i.e. a non repeating link to or from the shuttle service
14773  {
14774  if(ReverseTDVectorNumber == ForwardTDVectorNumber)
14775  {
14776  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode +
14777  " appears in the same sequence as the corresponding shuttle service " + MainHeadCode);
14778  TrainDataVector.clear();
14779  Utilities->CallLogPop(1071);
14780  return(false);
14781  }
14782  }
14783 /* it's allowed to have a different description
14784  if((ForwardEntryPtr->Command == "F-nshs") || (ForwardEntryPtr->Command == "Sns-fsh"))//i.e. a non repeating link to or from the shuttle service
14785  {
14786  if((MainTrainDataPtr->Description != "") && (OtherTrainDataPtr->Description != "") && (MainTrainDataPtr->Description != OtherTrainDataPtr->Description))
14787  {
14788  SecondPassMessage(GiveMessages, "Error in timetable - the non repeating link service " + NonRepeatingHeadCode + " has a different description to the corresponding shuttle service " + MainHeadCode);
14789  TrainDataVector.clear();
14790  Utilities->CallLogPop(1072);
14791  return false;
14792  }
14793  }
14794 */
14795  if(ForwardEntryPtr->Command == "Sns-sh")
14796  {
14797  if(ReverseEntryPtr->Command != "F-nshs")
14798  {
14799  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'F-nshs' event for the 'Sns-sh' train whose headcode is " +
14800  MainHeadCode + " and is a new shuttle service formed from the service with headcode " + NonRepeatingHeadCode);
14801  TrainDataVector.clear();
14802  Utilities->CallLogPop(1073);
14803  return(false);
14804  }
14805  }
14806  if(ForwardEntryPtr->Command == "F-nshs")
14807  {
14808  if(ReverseEntryPtr->Command != "Sns-sh")
14809  {
14810  SecondPassMessage(GiveMessages, "Error in timetable - unable to find a corresponding 'Sns-sh' event for the 'F-nshs' train whose headcode is " +
14811  MainHeadCode + " and forms a new shuttle service with headcode " + NonRepeatingHeadCode);
14812  TrainDataVector.clear();
14813  Utilities->CallLogPop(1074);
14814  return(false);
14815  }
14816  else
14817  {
14818  ForwardEntryPtr->LinkedTrainEntryPtr = OtherTrainDataPtr;
14819  ReverseEntryPtr->NonRepeatingShuttleLinkEntryPtr = MainTrainDataPtr;
14820  if(OtherTrainDataPtr->Description == "")
14821  {
14822  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14823  }
14824  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14825  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14826  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14827  }
14828  }
14829  if(ForwardEntryPtr->Command == "Sns-fsh")
14830  {
14831  if(ReverseEntryPtr->Command != "Fns-sh")
14832  {
14833  SecondPassMessage(GiveMessages,
14834  "Error in timetable - unable to find a corresponding 'Fns-sh' event for the 'Sns-fsh' non-shuttle service whose headcode is " + MainHeadCode +
14835  " formed from a shuttle service with headcode " + NonRepeatingHeadCode);
14836  TrainDataVector.clear();
14837  Utilities->CallLogPop(1075);
14838  return(false);
14839  }
14840  }
14841  if(ForwardEntryPtr->Command == "Fns-sh")
14842  {
14843  if(ReverseEntryPtr->Command != "Sns-fsh")
14844  {
14845  SecondPassMessage(GiveMessages,
14846  "Error in timetable - unable to find a corresponding 'Sns-fsh' event for the 'Fns-sh' shuttle service whose headcode is " + MainHeadCode +
14847  " and forms a new non-shuttle service with headcode " + NonRepeatingHeadCode);
14848  TrainDataVector.clear();
14849  Utilities->CallLogPop(1076);
14850  return(false);
14851  }
14852  else
14853  {
14854  ForwardEntryPtr->NonRepeatingShuttleLinkEntryPtr = OtherTrainDataPtr;
14855  // links to the non-repeating non-shuttle linked service
14856  ReverseEntryPtr->LinkedTrainEntryPtr = MainTrainDataPtr;
14857  // needed for creating formatted timetable
14858  if(OtherTrainDataPtr->Description == "")
14859  {
14860  OtherTrainDataPtr->Description = MainTrainDataPtr->Description;
14861  }
14862  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14863  OtherTrainDataPtr->MaxRunningSpeed = MainTrainDataPtr->MaxRunningSpeed;
14864  // Probably redundant as this is continued from the earlier service when the changeover happens (also may not be set here yet if a service continuation)
14865  }
14866  }
14867  Utilities->CallLogPop(1077);
14868  return(true);
14869 }
14870 
14871 // ---------------------------------------------------------------------------
14872 
14873 bool TTrainController::CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes, int RepeatNumber)
14874 // Forward train is the finish shuttle entry 'Fns-sh'.
14875 // The Reverse (new non-repeating service) time must == Forward time + (RepeatMins * RepeatNumber) but allow 10 secs either side
14876 {
14877  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckNonRepeatingShuttleLinkTime," + AnsiString(double(ForwardEventTime))
14878  + "," + AnsiString(double(ReverseEventTime)) + "," + AnsiString(RepeatMinutes) + "," + AnsiString(RepeatNumber));
14879  int ForwardSecs = int(double(ForwardEventTime) * 86400);
14880  int ReverseSecs = int(double(ReverseEventTime) * 86400);
14881  int RepeatSecs = RepeatMinutes * RepeatNumber * 60;
14882 
14883  if((ReverseSecs > (ForwardSecs + RepeatSecs + 10)) || (ReverseSecs < (ForwardSecs + RepeatSecs - 10)))
14884  {
14885  Utilities->CallLogPop(1369);
14886  return(false);
14887  }
14888  else
14889  {
14890  Utilities->CallLogPop(1370);
14891  return(true);
14892  }
14893 }
14894 
14895 // ---------------------------------------------------------------------------
14896 
14897 bool TTrainController::CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
14898 // check that each shuttle start ends either in Fns or Fxx-sh (though a single service can't end in Fxx-sh), and that
14899 // when the Fxx-sh is reached it references the original start and not another shuttle - not allowed to link two shuttles,
14900 // don't ever need to and as designed would skip repeats.
14901 
14902 // enter with TDEntry a shuttle start - Snt-sh or Sns-sh
14903 {
14904  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckShuttleServiceIntegrity," + AnsiString(TDEntryPtr->HeadCode));
14905  if(TDEntryPtr->ActionVector.back().FormatType != Repeat)
14906  {
14907  throw Exception("Error - last entry in " + TDEntryPtr->HeadCode + " service is not a repeat - should have already found this error");
14908  }
14909  TTrainDataEntry *ShuttleStartAddress = TDEntryPtr;
14910  AnsiString OriginalHeadCode = TDEntryPtr->HeadCode;
14911  AnsiString LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14912 
14913  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14914  {
14915  SecondPassMessage(GiveMessages, "Error in timetable - last event in shuttle service " + TDEntryPtr->HeadCode + " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14916  TrainDataVector.clear();
14917  Utilities->CallLogPop(1091);
14918  return(false);
14919  }
14920  while(LastActionCommand == "Fns")
14921  {
14922  TDEntryPtr = (TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr;
14923  LastActionCommand = (TDEntryPtr->ActionVector.end() - 2)->Command;
14924  if((LastActionCommand != "Fns") && (LastActionCommand != "Fns-sh") && (LastActionCommand != "Frh-sh"))
14925  {
14926  SecondPassMessage(GiveMessages,
14927  "Error in timetable - last event in a continuation shuttle service (i.e links back to a shuttle) whose headcode is " + TDEntryPtr->HeadCode +
14928  " is not 'Fns', 'Fns-sh' or 'Frh-sh'");
14929  TrainDataVector.clear();
14930  Utilities->CallLogPop(1092);
14931  return(false);
14932  }
14933  }
14934  // exit the 'while' with LastActionCommand FSH-XX
14935  if((TDEntryPtr->ActionVector.end() - 2)->LinkedTrainEntryPtr != ShuttleStartAddress)
14936  {
14937  SecondPassMessage(GiveMessages, "Error in timetable - the event that ends service " + TDEntryPtr->HeadCode +
14938  " is a shuttle finish, but it doesn't link back to the start of the original shuttle starting service " + OriginalHeadCode +
14939  ". The linking of two or more shuttles is not permitted.");
14940  TrainDataVector.clear();
14941  Utilities->CallLogPop(1093);
14942  return(false);
14943  }
14944  Utilities->CallLogPop(1094);
14945  return(true);
14946 }
14947 
14948 // ---------------------------------------------------------------------------
14949 
14950 void TTrainController::TimetableMessage(bool GiveMessages, AnsiString Message)
14951 {
14952  if(!GiveMessages)
14953  {
14954  return;
14955  }
14956  // if(ServiceReference == "") ShowMessage(Message);
14957  if(!CheckHeadCodeValidity(12, false, ServiceReference))
14958  {
14959  ShowMessage(Message);
14960  }
14961  // changed from above at v2.3.0 as a meaningless value for 'Timetable invalid - unable to find a valid start time on its own line' (uses last entry text)
14962  // false means don't give messages within the function
14963  else
14964  {
14965  ShowMessage("Service " + ServiceReference + ": " + Message);
14966  }
14967 }
14968 
14969 // ---------------------------------------------------------------------------
14970 
14971 void TTrainController::SecondPassMessage(bool GiveMessages, AnsiString Message)
14972 {
14973  if(!GiveMessages)
14974  {
14975  return;
14976  }
14977  ShowMessage(Message);
14978 }
14979 
14980 // $$$$$$$$$$$$$$$$$$$$$$$ End of Timetable Functions $$$$$$$$$$$$$$$$$$$$$$$
14981 // ---------------------------------------------------------------------------
14982 
14983 void TTrainController::LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
14984 // FailTrainEntry: 06:00:10 HELD: 2F43 can't enter railway, train obstructing entry position 57-N5
14985 // FailCreateTrain: 06:00:10 HELD: 2F43 can't be created, train obstructing start position 57-N5
14986 // FailCreateLockedRoute: 06:00:10 HELD: 2F43 can't be created on a locked route - start position 57-N5
14987 // FailEnterLockedRoute: 06:00:10 HELD: 2F43 can't enter on a locked route - start position 57-N5
14988 // FailCreatePoints: 06:00:10 HELD: 2F43 can't be created, points set to diverge at start position 57-N5
14989 // FailUnexpectedExitRailway: 06:00:10 ERROR: 2F43 left railway unexpectedly at position 57-N5
14990 // FailIncorrectExit: 06:00:10 ERROR: 2F43 left railway at an incorrect exit at position 57-N5
14991 // FailSPAD: 06:00:10 ERROR: 2F43 PASSED SIGNAL AT DANGER at position 57-N5
14992 // FailLockedRoute: 06:00:10 ERROR: SPAD Risk! Signals reset ahead of train, at position 57-N5
14993 // FailLocTooShort: 06:00:10 ERROR: 2F43 failed to split - location too short at Essex Road
14994 // FailSplitDueToOtherTrain: 06:00:10 HELD: 2F43 unable to split - another train is obstructing at Essex Road
14995 // FailCrashed: 06:00:10: ERROR: 2F43 CRASHED INTO 3F43 at position 46-N7
14996 // FailDerailed: 06:00:10: ERROR: 2F43 DERAILED at position 46-N7
14997 // FailUnexpectedBuffers: 06:00:10: ERROR: 2F43 stopped at buffers unexpectedly at position 46-N7
14998 // FailMissedArrival: 06:00:10: ERROR: 2F43 failed to stop at Essex Road;
14999 // FailMissedSplit: 06:00:10: ERROR: 2F43 failed to split at Essex Road
15000 // FailMissedJBO: 06:00:10: ERROR: 2F43 failed to be joined by join other train at Essex Road
15001 // FailMissedJoinOther: 06:00:10: ERROR: 2F43 failed to join other train at Essex Road
15002 // FailMissedTerminate: 06:00:10: ERROR: 2F43 failed to terminate at Essex Road
15003 // FailMissedNewService: 06:00:10: ERROR: 2F43 failed to form new service at Essex Road
15004 // FailMissedExitRailway: 06:00:10: ERROR: 2F43 failed to exit railway
15005 // FailMissedChangeDirection: 06:00:10: ERROR: 2F43 failed to change direction at Essex Road
15006 // FailMissedPass: 06:00:10: ERROR: 2F43 failed to pass Essex Road
15007 // FailBuffersPreventingStart: 06:00:10: ERROR: 2F43 facing buffers and unable to start at Essex Road
15008 // FailBufferCrash: 06:00:10: ERROR: 2F43 CRASHED INTO BUFFERS at 46-N7
15009 // FailLevelCrossingCrash: 06:00:10: ERROR: 2F43 CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at 46-N7
15010 // RouteForceCancelled: 06:00:10: ERROR: 2F43 forced a route cancellation by occupying it incorrectly at 46-N7
15011 // WaitingForJBO: 06:00:10: WARNING: 2F43 waiting to join 3F43 at Essex Road
15012 // WaitingForFJO: 06:00:10: WARNING: 2F43 waiting to be joined by 3F43 at Essex Road
15013 // FailEntryRouteSetAgainst: 06:00:10: WARNING: 2F43 can't enter railway, route set against it at entry position 57-N5 //added at v2.9.1
15014 {
15015  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LogActionError," + HeadCode + "," + OtherHeadCode + "," +
15016  AnsiString(ActionEventType) + "," + LocationID);
15017  AnsiString BaseLog = "", Prefix = "", ErrorLog = "", WarningStr = "";
15018 
15019  TDateTime ActualTime = TrainController->TTClockTime; //moved from lower down at v2.9.1
15020  AnsiString TimeAndHeadCode = Utilities->Format96HHMMSS(ActualTime) + " " + HeadCode; //added at v2.9.1 to give more info to user
15021 
15022  Prefix = " ERROR: ";
15023  if(ActionEventType == FailTrainEntry)
15024  {
15025  Prefix = " HELD: ";
15026  ErrorLog = " can't enter railway, train obstructing entry position ";
15027  WarningStr = " can't enter railway, train obstructing entry position ";
15028  Display->WarningLog(1, TimeAndHeadCode + WarningStr + LocationID);
15029  }
15030  else if(ActionEventType == FailEntryRouteSetAgainst) //added at v2.9.1
15031  {
15032  Prefix = " HELD: ";
15033  ErrorLog = " can't enter railway, route set against it at entry position ";
15034  WarningStr = " can't enter railway, route set against it at entry position ";
15035  Display->WarningLog(10, TimeAndHeadCode + WarningStr + LocationID);
15036  }
15037  else if(ActionEventType == FailCreateTrain)
15038  {
15039  Prefix = " HELD: ";
15040  ErrorLog = " can't be created, train obstructing ";
15041  WarningStr = " can't be created, train obstructing ";
15042  Display->WarningLog(2, TimeAndHeadCode + WarningStr + LocationID);
15043  }
15044  else if(ActionEventType == FailCreateLockedRoute)
15045  {
15046  Prefix = " HELD: ";
15047  ErrorLog = " can't be created on a locked route at ";
15048  WarningStr = " can't be created on a locked route at ";
15049  Display->WarningLog(4, TimeAndHeadCode + WarningStr + LocationID);
15050  }
15051  else if(ActionEventType == FailEnterLockedRoute)
15052  {
15053  Prefix = " HELD: ";
15054  ErrorLog = " can't enter on a locked route at ";
15055  WarningStr = " can't enter on a locked route at ";
15056  Display->WarningLog(5, TimeAndHeadCode + WarningStr + LocationID);
15057  }
15058  else if(ActionEventType == FailCreatePoints)
15059  {
15060  Prefix = " HELD: ";
15061  ErrorLog = " can't be created, diverging points at ";
15062  WarningStr = " can't be created, diverging points at ";
15063  Display->WarningLog(3, TimeAndHeadCode + WarningStr + LocationID);
15064  }
15065  else if(ActionEventType == FailUnexpectedExitRailway)
15066  {
15067  ErrorLog = " left railway unexpectedly at ";
15068  UnexpectedExits++;
15069  }
15070  else if(ActionEventType == FailIncorrectExit)
15071  {
15072  ErrorLog = " left railway at an incorrect exit at ";
15073  IncorrectExits++;
15074  }
15075  else if(ActionEventType == FailLocTooShort)
15076  {
15077  ErrorLog = " failed to split - location too short at ";
15078  WarningStr = " failed to split, location too short at ";
15079  Display->WarningLog(6, TimeAndHeadCode + WarningStr + LocationID);
15080  }
15081  else if(ActionEventType == FailSplitDueToOtherTrain)
15082  {
15083  Prefix = " HELD: ";
15084  ErrorLog = " unable to split - other train obstructing at ";
15085  WarningStr = " unable to split - other train obstructing at ";
15086  Display->WarningLog(7, TimeAndHeadCode + WarningStr + LocationID);
15087  }
15088  else if(ActionEventType == FailUnexpectedBuffers)
15089  {
15090  ErrorLog = " stopped at buffers unexpectedly at position ";
15091  }
15092  else if(ActionEventType == FailMissedArrival)
15093  {
15094  ErrorLog = " failed to stop at ";
15095  MissedStops++;
15096  }
15097  else if(ActionEventType == FailMissedSplit)
15098  {
15099  ErrorLog = " failed to split at ";
15101  }
15102  else if(ActionEventType == FailMissedJBO)
15103  {
15104  ErrorLog = " failed to be joined by other train at ";
15106  }
15107  else if(ActionEventType == FailMissedJoinOther)
15108  {
15109  ErrorLog = " failed to join other train at ";
15111  }
15112  else if(ActionEventType == FailMissedTerminate)
15113  {
15114  ErrorLog = " failed to terminate at ";
15116  }
15117  else if(ActionEventType == FailMissedNewService)
15118  {
15119  ErrorLog = " failed to form new service at ";
15121  }
15122  else if(ActionEventType == FailMissedExitRailway)
15123  {
15124  ErrorLog = " failed to exit railway ";
15126  }
15127  else if(ActionEventType == FailMissedChangeDirection)
15128  {
15129  ErrorLog = " failed to change direction at ";
15130 // OtherMissedEvents++; //dropped at v2.12.0 as cdt shouldn't count
15131  }
15132  else if(ActionEventType == FailMissedPass)
15133  {
15134  ErrorLog = " failed to pass ";
15135 // OtherMissedEvents++; //dropped at v2.12.0 as missed pass shouldn't count
15136  }
15137  else if(ActionEventType == FailBuffersPreventingStart)
15138  {
15139  ErrorLog = " facing buffers and unable to start at ";
15140  }
15141  else if(ActionEventType == FailDerailed)
15142  {
15143  ErrorLog = " DERAILED at position ";
15144  Prefix = " DERAILMENT: ";
15145  Derailments++;
15146  }
15147  else if(ActionEventType == FailBufferCrash)
15148  {
15149  ErrorLog = " CRASHED INTO BUFFERS at ";
15150  Prefix = " CRASH: ";
15151  CrashedTrains++;
15152  }
15153  else if(ActionEventType == FailLevelCrossingCrash)
15154  {
15155  ErrorLog = " CRASHED INTO ROAD TRAFFIC AT A LEVEL CROSSING at ";
15156  Prefix = " CRASH: ";
15157  CrashedTrains++;
15158  }
15159  else if(ActionEventType == FailCrashed)
15160  {
15161  ErrorLog = " CRASHED INTO " + OtherHeadCode + " at position ";
15162  Prefix = " CRASH: ";
15163  CrashedTrains++;
15164  CrashedTrains++;
15165  }
15166  else if(ActionEventType == FailSPAD)
15167  {
15168  ErrorLog = " PASSED SIGNAL AT DANGER at position ";
15169  Prefix = " SPAD: ";
15170  SPADEvents++;
15171  }
15172  else if(ActionEventType == FailLockedRoute)
15173  {
15174  ErrorLog = "Signals reset ahead of train, route cancelled at position ";
15175  Prefix = " SPAD RISK: ";
15176  SPADRisks++;
15177  }
15178  else if(ActionEventType == RouteForceCancelled)
15179  {
15180  ErrorLog = " forced a route cancellation by occupying it incorrectly at ";
15181  }
15182  else if(ActionEventType == WaitingForJBO)
15183  {
15184  Prefix = " WARNING: ";
15185  ErrorLog = " waiting to join " + OtherHeadCode + " at ";
15186  WarningStr = " waiting to join " + OtherHeadCode + " at ";
15187  Display->WarningLog(8, TimeAndHeadCode + WarningStr + LocationID);
15188  }
15189  else if(ActionEventType == WaitingForFJO)
15190  {
15191  Prefix = " WARNING: ";
15192  ErrorLog = " waiting to be joined by " + OtherHeadCode + " at ";
15193  WarningStr = " waiting to be joined by " + OtherHeadCode + " at ";
15194  Display->WarningLog(9, TimeAndHeadCode + WarningStr + LocationID);
15195  }
15196 
15197  BaseLog = Utilities->Format96HHMMSS(ActualTime) + Prefix + HeadCode;
15198  Display->PerformanceLog(4, BaseLog + ErrorLog + LocationID);
15199  Utilities->CallLogPop(1371);
15200 }
15201 
15202 // ---------------------------------------------------------------------------
15203 
15205 {
15206 /*
15207  TrainDataEntry
15208  AnsiString HeadCode, Description;//null on creation
15209  int StartSpeed, MaxRunningSpeed;//both kph
15210  int RepeatNumber;
15211  TActionVector ActionVector;
15212  TTrainOperatingDataVector TrainOperatingDataVector;//no of repeats + 1
15213  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; RepeatNumber=0;}
15214 
15215  ActionVectorEntry
15216  TTimetableEntryType FormatType;
15217  TDateTime EventTime, ArrivalTime, DepartureTime;//zeroed on creation so change to -1 as a marker for 'not set'
15218  AnsiString LocationName, Command, OtherHeadCode;//null on creation
15219  TActionVectorEntry *OtherHeadCodeStartingEntryPtr;
15220  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15221  int RepeatNumber;
15222 
15223  TrainOperatingData
15224  int Mass, MaxBrakeRate, PowerAtRail;//kg;m/s/s;W
15225  int TrainID;
15226  TRunningEntry RunningEntry;
15227  TDateTime StartTime;
15228  AnsiString HeadCode;
15229 */
15230  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveTrainDataVectorToFile");
15231  std::ofstream OutFile("TrainData.csv");
15232 
15233  if(OutFile == 0)
15234  {
15235  ShowMessage("Output file TrainData.csv failed to open");
15236  Utilities->CallLogPop(1372);
15237  return;
15238  }
15239  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15240  {
15241  const TTrainDataEntry &TDEntry = TrainDataVector.at(x);
15242  OutFile << "HeadCode" << ',' << "Description" << ',' << "StartSpeed" << ',' << "MaxRunningSpeed" << ',' << "NumberOfTrains" << '\n' << '\n';
15243 
15244  OutFile << TDEntry.HeadCode.c_str() << ',' << TDEntry.Description.c_str()
15245  << ',' << TDEntry.StartSpeed << ',' << TDEntry.MaxRunningSpeed << ',' << TDEntry.NumberOfTrains << '\n' << '\n';
15246 
15247  OutFile << ',' << "FormatType" << ',' << "EventTime" << ',' << "ArrivalTime" << ',' << "DepartureTime" << ',' << "LocationName" << ',' << "Command" <<
15248  ',' << "OtherHeadCode" << ',' << "LinkedTrainEntryPtr" << ',' << "RearStartOrRepeatMins" << ',' << "FrontStartOrRepeatDigits" << ',' <<
15249  "RepeatNumber" << '\n' << '\n';
15250  for(unsigned int y = 0; y < TrainDataVector.at(x).ActionVector.size(); y++)
15251  {
15252  TActionVectorEntry &AVEntry = TrainDataVector.at(x).ActionVector.at(y);
15253  AnsiString TimetableEntryTypeStr;
15254  // NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere, FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat
15255  switch(AVEntry.FormatType)
15256  {
15257  case 0:
15258  {
15259  TimetableEntryTypeStr = "NoFormat";
15260  break;
15261  }
15262 
15263  case 1:
15264  {
15265  TimetableEntryTypeStr = "TimeLoc";
15266  break;
15267  }
15268 
15269  case 2:
15270  {
15271  TimetableEntryTypeStr = "TimeTimeLoc";
15272  break;
15273  }
15274 
15275  case 3:
15276  {
15277  TimetableEntryTypeStr = "TimeCmd";
15278  break;
15279  }
15280 
15281  case 4:
15282  {
15283  TimetableEntryTypeStr = "StartNew";
15284  break;
15285  }
15286 
15287  case 5:
15288  {
15289  TimetableEntryTypeStr = "TimeCmdHeadCode";
15290  break;
15291  }
15292 
15293  case 6:
15294  {
15295  TimetableEntryTypeStr = "FinRemHere";
15296  break;
15297  }
15298 
15299  case 7:
15300  {
15301  TimetableEntryTypeStr = "FNSShuttle";
15302  break;
15303  }
15304 
15305  case 8:
15306  {
15307  TimetableEntryTypeStr = "SNTShuttle";
15308  break;
15309  }
15310 
15311  case 9:
15312  {
15313  TimetableEntryTypeStr = "SNSShuttle";
15314  break;
15315  }
15316 
15317  case 10:
15318  {
15319  TimetableEntryTypeStr = "SNSNonRepeatFromShuttle";
15320  break;
15321  }
15322 
15323  case 11:
15324  {
15325  TimetableEntryTypeStr = "FSHNewService";
15326  break;
15327  }
15328 
15329  case 12:
15330  {
15331  TimetableEntryTypeStr = "Repeat";
15332  break;
15333  }
15334 
15335  default:
15336  {
15337  TimetableEntryTypeStr = "Default";
15338  break;
15339  }
15340  }
15341  OutFile << ',' << TimetableEntryTypeStr.c_str() << ',' << Utilities->Format96HHMM(AVEntry.EventTime).c_str() << ',' << Utilities->Format96HHMM
15342  (AVEntry.ArrivalTime).c_str() << ',' << Utilities->Format96HHMM(AVEntry.DepartureTime).c_str() << ',' << AVEntry.LocationName.c_str()
15343  << ',' << AVEntry.Command.c_str() << ',' << AVEntry.OtherHeadCode.c_str()
15344  << ',' << AVEntry.LinkedTrainEntryPtr << ',' << AVEntry.RearStartOrRepeatMins << ',' << AVEntry.FrontStartOrRepeatDigits << ',' <<
15345  AVEntry.NumberOfRepeats << '\n';
15346  }
15347  OutFile << '\n';
15348  OutFile << ',' << ',' << "Mass" << ',' << "MaxBrakeRate" << ',' << "PowerAtRail" << ',' << "TrainID" << ',' << "RunningEntry" << '\n' << '\n';
15349  for(unsigned int y = 0; y < TrainDataVector.at(x).TrainOperatingDataVector.size(); y++)
15350  {
15351  TTrainOperatingData TOD = TrainDataVector.at(x).TrainOperatingDataVector.at(y);
15352  AnsiString RunningEntryStr;
15353  // NotStarted, Running, Exited
15354  switch(TOD.RunningEntry)
15355  {
15356  case 0:
15357  {
15358  RunningEntryStr = "NotStarted";
15359  break;
15360  }
15361 
15362  case 1:
15363  {
15364  RunningEntryStr = "Running";
15365  break;
15366  }
15367 
15368  case 2:
15369  {
15370  RunningEntryStr = "Exited";
15371  break;
15372  }
15373  }
15374  OutFile << ',' << ',' << TOD.TrainID << ',' << RunningEntryStr.c_str() << ',' << '\n';
15375  }
15376  OutFile << '\n';
15377  }
15378  OutFile.close();
15379  Utilities->CallLogPop(1373);
15380 }
15381 
15382 // ---------------------------------------------------------------------------
15383 
15384 void TTrainController::StopTTClockMessage(int Caller, AnsiString Message)
15385 // ShowMessage stops everything so this function used where a message is needed when may be in Operating mode.
15386 // The timetable Restart and BaseTimes are reset so the timetable clock stops & restarts when 'OK' button pressed
15387 {
15388  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",StopTTClockMessage," + Message);
15389  StopTTClockFlag = true; // so TTClock stopped during MasterClockTimer function
15391  ShowMessage(Message);
15392  BaseTime = TDateTime::CurrentDateTime();
15393  StopTTClockFlag = false;
15394  Utilities->CallLogPop(1374);
15395 }
15396 
15397 // ---------------------------------------------------------------------------
15398 
15399 void TTrainController::SaveSessionTrains(int Caller, std::ofstream &SessionFile)
15400 // save *TrainDataEntryPtr & *ActionVectorEntryPtr as integer offsets
15401 // from the start of the relevant vectors. Can't save the pointer values
15402 // as these will be different each time the vectors are created
15403 {
15404  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionTrains");
15405  Utilities->SaveFileInt(SessionFile, TrainVector.size());
15406  for(unsigned int x = 0; x < TrainVector.size(); x++)
15407  {
15408  TrainVectorAt(55, x).SaveOneSessionTrain(0, SessionFile);
15409  }
15410  Utilities->CallLogPop(1375);
15411 }
15412 
15413 // ---------------------------------------------------------------------------
15414 
15415 void TTrainController::LoadSessionTrains(int Caller, std::ifstream &SessionFile)
15416 {
15417  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionTrains");
15418  int NumberOfTrains = Utilities->LoadFileInt(SessionFile);
15419  TTrain *NewTrain = new TTrain(1, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15420  // by zero error in calculating AValue, use 1
15421  for(int x = 0; x < NumberOfTrains; x++)
15422  {
15423  *NewTrain = TTrain(2, 0, 0, "", 0, 1, 0, 0, 0, (TTrainMode)0, 0, 0, 0, 0, 0); // have to have >0 for mass, else have divide
15424  // by zero error in calculating AValue, use 1
15425  NewTrain->LoadOneSessionTrain(0, SessionFile);
15426  if((NewTrain->EntrySpeed < 1) && (NewTrain->PowerAtRail < 1))
15427  // added at v2.4.0. have to include as that value not stored in session file
15428  {
15429  NewTrain->StoppedWithoutPower = true;
15430  }
15431  TrainVector.push_back(*NewTrain);
15432  LastTrainLoaded = x;
15433  }
15434  delete NewTrain;
15435  Utilities->CallLogPop(1376);
15436 }
15437 
15438 // ---------------------------------------------------------------------------
15439 
15440 bool TTrainController::CheckSessionTrains(int Caller, std::ifstream &InFile)
15441 {
15442  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionTrains");
15443  int NumberOfTrains;
15444 
15445  if(!Utilities->CheckAndReadFileInt(InFile, 0, 10000, NumberOfTrains))
15446  {
15447  Utilities->CallLogPop(1377);
15448  return(false);
15449  }
15450  for(int x = 0; x < NumberOfTrains; x++)
15451  {
15452  if(!(TTrain::CheckOneSessionTrain(InFile)))
15453  {
15454  Utilities->CallLogPop(1378);
15455  return(false);
15456  }
15457  }
15458  Utilities->CallLogPop(1379);
15459  return(true);
15460 }
15461 
15462 // ---------------------------------------------------------------------------
15463 
15464 void TTrainController::SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
15465 {
15466  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionLockedRoutes");
15467  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.size());
15468  for(unsigned int x = 0; x < AllRoutes->LockedRouteVector.size(); x++)
15469  {
15470  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).RouteNumber);
15471  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).TruncateTrackVectorPosition);
15472  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastTrackVectorPosition);
15473  Utilities->SaveFileInt(SessionFile, AllRoutes->LockedRouteVector.at(x).LastXLinkPos);
15474  Utilities->SaveFileDouble(SessionFile, double(AllRoutes->LockedRouteVector.at(x).LockStartTime));
15475  }
15476  Utilities->CallLogPop(1380);
15477 }
15478 
15479 // ---------------------------------------------------------------------------
15480 
15481 void TTrainController::LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15482 {
15483  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionLockedRoutes");
15484  TAllRoutes::TLockedRouteClass LockedRouteObject;
15485  int LockedRouteVectorSize = Utilities->LoadFileInt(SessionFile);
15486 
15487  for(int x = 0; x < LockedRouteVectorSize; x++)
15488  {
15489  LockedRouteObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15490  LockedRouteObject.TruncateTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15491  LockedRouteObject.LastTrackVectorPosition = Utilities->LoadFileInt(SessionFile);
15492  LockedRouteObject.LastXLinkPos = Utilities->LoadFileInt(SessionFile);
15493  double LockStartTimeDouble = Utilities->LoadFileDouble(SessionFile);
15494  LockedRouteObject.LockStartTime = TDateTime(LockStartTimeDouble);
15495  AllRoutes->LockedRouteVector.push_back(LockedRouteObject);
15496  }
15497  Utilities->CallLogPop(1381);
15498 }
15499 
15500 // ---------------------------------------------------------------------------
15501 
15502 bool TTrainController::CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
15503 {
15504  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionLockedRoutes");
15505  int LockedRouteVectorSize;
15506 
15507  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, LockedRouteVectorSize))
15508  {
15509  Utilities->CallLogPop(1382);
15510  return(false);
15511  }
15512  for(int x = 0; x < LockedRouteVectorSize; x++)
15513  {
15514  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15515  {
15516  Utilities->CallLogPop(1383);
15517  return(false);
15518  }
15519  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15520  {
15521  Utilities->CallLogPop(1384);
15522  return(false);
15523  }
15524  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15525  {
15526  Utilities->CallLogPop(1385);
15527  return(false);
15528  }
15529  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15530  {
15531  Utilities->CallLogPop(1386);
15532  return(false);
15533  }
15534  if(!Utilities->CheckFileDouble(SessionFile))
15535  {
15536  Utilities->CallLogPop(1387);
15537  return(false);
15538  }
15539  }
15540  Utilities->CallLogPop(1388);
15541  return(true);
15542 }
15543 
15544 // ---------------------------------------------------------------------------
15545 
15546 void TTrainController::SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
15547 {
15548  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SaveSessionContinuationAutoSigEntries");
15549  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.size());
15550  for(unsigned int x = 0; x < ContinuationAutoSigVector.size(); x++)
15551  {
15552  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).RouteNumber);
15553  Utilities->SaveFileInt(SessionFile, ContinuationAutoSigVector.at(x).AccessNumber);
15554  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).FirstDelay);
15555  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).SecondDelay);
15556  Utilities->SaveFileDouble(SessionFile, ContinuationAutoSigVector.at(x).ThirdDelay);
15557  Utilities->SaveFileDouble(SessionFile, double(ContinuationAutoSigVector.at(x).PassoutTime));
15558  }
15559  Utilities->CallLogPop(1389);
15560 }
15561 
15562 // ---------------------------------------------------------------------------
15563 
15564 void TTrainController::LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15565 {
15566  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",LoadSessionContinuationAutoSigEntries");
15567  TContinuationAutoSigEntry ContinuationAutoSigObject;
15568  int ContinuationAutoSigVectorSize = Utilities->LoadFileInt(SessionFile);
15569 
15570  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15571  {
15572  ContinuationAutoSigObject.RouteNumber = Utilities->LoadFileInt(SessionFile);
15573  ContinuationAutoSigObject.AccessNumber = Utilities->LoadFileInt(SessionFile);
15574  ContinuationAutoSigObject.FirstDelay = Utilities->LoadFileDouble(SessionFile);
15575  ContinuationAutoSigObject.SecondDelay = Utilities->LoadFileDouble(SessionFile);
15576  ContinuationAutoSigObject.ThirdDelay = Utilities->LoadFileDouble(SessionFile);
15577  double PassoutTimeDouble = Utilities->LoadFileDouble(SessionFile);
15578  ContinuationAutoSigObject.PassoutTime = TDateTime(PassoutTimeDouble);
15579  ContinuationAutoSigVector.push_back(ContinuationAutoSigObject);
15580  }
15581  Utilities->CallLogPop(1390);
15582 }
15583 
15584 // ---------------------------------------------------------------------------
15585 
15586 bool TTrainController::CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
15587 {
15588  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CheckSessionContinuationAutoSigEntries");
15589  int ContinuationAutoSigVectorSize;
15590 
15591  if(!Utilities->CheckAndReadFileInt(SessionFile, 0, 10000, ContinuationAutoSigVectorSize))
15592  {
15593  Utilities->CallLogPop(1391);
15594  return(false);
15595  }
15596  for(int x = 0; x < ContinuationAutoSigVectorSize; x++)
15597  {
15598  if(!Utilities->CheckFileInt(SessionFile, 0, 1000000))
15599  {
15600  Utilities->CallLogPop(1392);
15601  return(false);
15602  }
15603  if(!Utilities->CheckFileInt(SessionFile, 0, 3))
15604  {
15605  Utilities->CallLogPop(1393);
15606  return(false);
15607  }
15608  if(!Utilities->CheckFileDouble(SessionFile))
15609  {
15610  Utilities->CallLogPop(1405);
15611  return(false);
15612  }
15613  if(!Utilities->CheckFileDouble(SessionFile))
15614  {
15615  Utilities->CallLogPop(1406);
15616  return(false);
15617  }
15618  if(!Utilities->CheckFileDouble(SessionFile))
15619  {
15620  Utilities->CallLogPop(1407);
15621  return(false);
15622  }
15623  if(!Utilities->CheckFileDouble(SessionFile))
15624  {
15625  Utilities->CallLogPop(1394);
15626  return(false);
15627  }
15628  }
15629  Utilities->CallLogPop(1395);
15630  return(true);
15631 }
15632 
15633 // ---------------------------------------------------------------------------
15634 
15635 /*
15636  class TContinuationTrainExpectationEntry //for expected trains at continuation entries
15637  {
15638  public:
15639  AnsiString Description; ///< service description
15640  AnsiString HeadCode; ///< service headcode
15641  int RepeatNumber; ///< service RepeatNumber
15642  int IncrementalMinutes; ///< Repeat separation in minutes
15643  int IncrementalDigits; ///< Repeat headcode separation
15644  int VectorPosition; ///< TrackVectorPosition for the continuation element
15645  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
15646  };
15647 
15648 
15649  typedef std::multimap<TDateTime,TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
15650  typedef pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair;
15651 */
15652 
15654 // build this into timetable load so session loading can use it too
15655 // being a multimap it automatically sorts in ascending EventTime order
15656 {
15657  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",BuildContinuationTrainExpectationMultiMap");
15659  // need to clear as this called twice when load a session
15660  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15661  {
15662  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
15663  const TActionVectorEntry &AVFirstEntry = TDEntry.ActionVector.at(0);
15664  const TActionVectorEntry &AVLastEntry = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
15665 
15666  if(AVFirstEntry.Command == "Snt")
15667  // new train (no need to include Snt-sh since they can't start at a continuation)
15668  {
15671  {
15673  CTEEntry.VectorPosition = AVFirstEntry.RearStartOrRepeatMins;
15674  // retains this value for all repeats
15675  CTEEntry.RepeatNumber = 0; // for first entry
15676  CTEEntry.TrainDataEntryPtr = &TDEntry;
15677  // retains this value for all repeats
15678  CTEEntry.HeadCode = TDEntry.HeadCode;
15679  CTEEntry.Description = TDEntry.Description;
15680  CTEEntry.IncrementalMinutes = 0;
15681  CTEEntry.IncrementalDigits = 0;
15682  if(AVLastEntry.FormatType == Repeat)
15683  {
15684  CTEEntry.IncrementalMinutes = AVLastEntry.RearStartOrRepeatMins;
15685  // retains this value or 0 for all repeats
15686  CTEEntry.IncrementalDigits = AVLastEntry.FrontStartOrRepeatDigits;
15687  // retains this value or 0 for all repeats
15688  }
15689  CTEMMP.first = AVFirstEntry.EventTime;
15690  CTEMMP.second = CTEEntry;
15691  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15692  // base entry
15693  if(TDEntry.NumberOfTrains > 1)
15694  {
15695  if(AVLastEntry.FormatType != Repeat)
15696  {
15697  throw Exception("Error, Last ActionVectorEntry not a repeat in BuildContinuationTrainExpectationMultiMap");
15698  }
15699  for(int y = 1; y < TDEntry.NumberOfTrains; y++)
15700  {
15701  CTEEntry.RepeatNumber = y;
15702  CTEEntry.HeadCode = GetRepeatHeadCode(23, TDEntry.HeadCode, y, AVLastEntry.FrontStartOrRepeatDigits);
15703  // CTEEntry.VectorPosition stays same
15704  CTEMMP.first = GetRepeatTime(3, AVFirstEntry.EventTime, y, AVLastEntry.RearStartOrRepeatMins);
15705  CTEMMP.second = CTEEntry;
15706  ContinuationTrainExpectationMultiMap.insert(CTEMMP);
15707  }
15708  }
15709  }
15710  }
15711  }
15712  Utilities->CallLogPop(1396);
15713 }
15714 
15715 // ---------------------------------------------------------------------------
15716 
15718 {
15719  // called when WarningFlashCount == 0 or when press zoomout button
15720  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",PlotTrainsInZoomOutMode");
15721  if(!Display->ZoomOutFlag)
15722  {
15723  Utilities->CallLogPop(1156);
15724  return;
15725  }
15726  for(unsigned int x = 0; x < TrainVector.size(); x++)
15727  {
15728  // plot blanks & track for all train, even if to be overplotted, since when flashing need to overplot all anyway
15729  // if OldPlotElement[x] == -1 then ignore (not plotted)
15731  TrainVectorAt(57, x).PlotTrainInZoomOutMode(0, Flash);
15732  }
15733  Display->Update();
15734  // need to keep this since Update() not called for PlotSmallOutput as too slow
15735  Utilities->CallLogPop(742);
15736 }
15737 
15738 // ---------------------------------------------------------------------------
15739 
15740 TTrain &TTrainController::TrainVectorAt(int Caller, int VecPos)
15741 {
15742  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",TrainVectorAt," + AnsiString(VecPos));
15743  if((VecPos < 0) || (VecPos >= (int)TrainVector.size()))
15744  {
15745  throw Exception("Out of Range Error, vector size: " + AnsiString(TrainVector.size()) + ", VecPos: " + AnsiString(VecPos) + " in TrainVectorAt");
15746  }
15747  Utilities->CallLogPop(740);
15748  return(TrainVector.at(VecPos));
15749 }
15750 
15751 // ---------------------------------------------------------------------------
15752 
15753 void TTrainController::CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
15754 {
15755  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateFormattedTimetable");
15756  AnsiString RetStr = "", PartStr = "";
15757 
15758 
15759 /*
15760  Have description & mass etc for train at top - header, then array of actions
15761 
15762  class TActionVectorEntry
15763  {
15764  public:
15765  AnsiString LocationName, Command, OtherHeadCode, NonRepeatingShuttleLinkHeadCode;
15767  bool SignallerControl;
15769  bool Warning;
15771  int NumberOfRepeats;
15773  int RearStartOrRepeatMins, FrontStartOrRepeatDigits;
15775  TDateTime EventTime, ArrivalTime, DepartureTime;
15777  TNumList ExitList;
15779  TTimetableFormatType FormatType;
15781  TTimetableLocationType LocationType;
15783  TTimetableSequenceType SequenceType;
15785  TTimetableShuttleLinkType ShuttleLinkType;
15787  TTrainDataEntry *LinkedTrainEntryPtr;
15789  TTrainDataEntry *NonRepeatingShuttleLinkEntryPtr;
15791 
15792  typedef std::vector<TActionVectorEntry> TActionVector;//contains all actions for a single train
15793 
15794  enum TRunningEntry {NotStarted, Running, Exited};//contains status info for each train
15795 
15796  class TTrainOperatingData
15797  {
15798  public:
15799  int TrainID;
15800  TActionEventType EventReported;
15801  TRunningEntry RunningEntry;
15802 
15803  //inline function
15804  TTrainOperatingData() {TrainID = -1; EventReported= NoEvent; RunningEntry=NotStarted;}//ID -1 = marker for not running
15805  };
15806 
15807  typedef std::vector<TTrainOperatingData> TTrainOperatingDataVector;
15808 
15809  class TTrainDataEntry
15810  {
15811  public:
15812  AnsiString HeadCode, ServiceReference, Description;
15814  double MaxBrakeRate;
15816  double MaxRunningSpeed;
15818  double PowerAtRail;
15820  int Mass;
15822  int NumberOfTrains;
15824  int SignallerSpeed;
15826  int StartSpeed;
15828  TActionVector ActionVector;
15830  TTrainOperatingDataVector TrainOperatingDataVector;
15832 
15833  //inline function
15834  TTrainDataEntry() {StartSpeed=0; MaxRunningSpeed=0; NumberOfTrains=0;}
15835  };
15836 
15837  typedef std::vector<TTrainDataEntry> TTrainDataVector;//object is a member of TTrainController & contains the whole timetable
15838 
15839  //formatted timetable types
15840  class TOneTrainFormattedEntry
15841  {
15842  AnsiString Action;//includes location if relevanr
15843  AnsiString Time;
15844  };
15845 
15846  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
15847 
15848  class TOneCompleteFormattedTrain//headcode + list of actions
15849  {
15850  public:
15851  AnsiString HeadCode;
15852  TOneFormattedTrainVector OneFormattedTrainVector;
15853  };
15854 
15855  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
15856 
15857  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
15858  {
15859  public:
15860  AnsiString Header;//description, mass, power, brake rate etc
15861  int NumberOfTrains;// number of repeats + 1
15862  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
15863  };
15864 
15865 
15866  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
15867  //end of formatted timetable types
15868 
15869 */
15870 
15871  AnsiString TTFileName = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
15872 
15873  // format "16/06/2009 20:55:17"
15874  // avoid characters in filename:= / \ : * ? " < > |
15875  TTFileName = CurDir + "\\Formatted timetables\\Timetable " + TTFileName + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
15876 
15877  AnsiString ShortTTName = "";
15878 
15879  for(int x = TTFileName.Length(); x > 0; x--)
15880  {
15881  if(TTFileName[x] == '\\')
15882  {
15883  ShortTTName = TTFileName.SubString(x + 1, TTFileName.Length() - x - 4);
15884  break;
15885  }
15886  }
15887 
15888  ShowMessage("Creates two timetables named " + ShortTTName +
15889  " in the 'Formatted timetables' folder, one in service order in '.csv' format, and one in chronological order in '.txt' format");
15890 
15891  Screen->Cursor = TCursor(-11); // Hourglass
15892 
15893  AnsiString FormatNoDPStr = "#######0";
15894  AnsiString TableTitle = "", TimetableTimeStr = "", MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "", FirstHeadCode = "", Header = "";
15895 
15897  TableTitle = "Railway: " + RailwayTitle + "; Timetable: " + TimetableTitle + "; Start time: " + TimetableTimeStr;
15898  TAllFormattedTrains *AllTTTrains = new TAllFormattedTrains;
15899 
15900  // all timetable in formatted form
15901  //create the AllTTTrains vector
15902  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
15903  {
15904  MassStr = "", PowerStr = "", BrakeStr = "", MaxSpeedStr = "";
15905  const TTrainDataEntry &TrainDataEntry = TrainDataVector.at(x);
15906  if(TrainDataEntry.Mass > 0)
15907  {
15908  MassStr = "; Mass " + AnsiString::FormatFloat(FormatNoDPStr, ((double)TrainDataEntry.Mass) / 1000) + "Te; ";
15909  }
15910  if(TrainDataEntry.PowerAtRail > 0)
15911  {
15912  PowerStr = "Power " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.PowerAtRail / 1000 / 0.8) + "kW; ";
15913  }
15914  if(TrainDataEntry.MaxBrakeRate > 0)
15915  {
15916  BrakeStr = "Brake force " + AnsiString::FormatFloat(FormatNoDPStr, (TrainDataEntry.MaxBrakeRate * TrainDataEntry.Mass / 9810)) + "Te; ";
15917  }
15918  if(TrainDataEntry.MaxRunningSpeed > 0)
15919  {
15920  MaxSpeedStr = "Maximum speed " + AnsiString::FormatFloat(FormatNoDPStr, TrainDataEntry.MaxRunningSpeed) + " km/h";
15921  }
15922  FirstHeadCode = TrainDataEntry.HeadCode;
15923  int IncDigits = 0, IncMinutes = 0;
15924  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
15925  if(!ActionVector.empty())
15926  {
15927  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
15928  {
15929  IncDigits = ActionVector.at(ActionVector.size() - 1).FrontStartOrRepeatDigits;
15930  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
15931  }
15932  }
15933  TTrainFormattedInformation OneTTLine;
15934  // contains all information for a single TT entry (including repeats)
15935  for(int y = 0; y < TrainDataEntry.NumberOfTrains; y++)
15936  {
15937  OneTTLine.Header = "";
15938  if((TrainDataEntry.Description != "") && (MassStr != ""))
15939  {
15940  OneTTLine.Header = TrainDataEntry.Description + MassStr + PowerStr + BrakeStr + MaxSpeedStr;
15941  }
15942  else if(TrainDataEntry.Description != "")
15943  {
15944  OneTTLine.Header = TrainDataEntry.Description;
15945  }
15946  OneTTLine.NumberOfTrains = TrainDataEntry.NumberOfTrains;
15947  TOneCompleteFormattedTrain OneTTTrain; // headcode + list of actions
15948  for(unsigned int z = 0; z < ActionVector.size(); z++)
15949  {
15950  TOneTrainFormattedEntry OneTTEntry;
15951  OneTTTrain.HeadCode = GetRepeatHeadCode(24, FirstHeadCode, y, IncDigits);
15952  TActionVectorEntry ActionVectorEntry = ActionVector.at(z);
15953  AnsiString PartStr = "", TimeStr = "";
15954 /*
15955  enum TTimetableFormatType {NoFormat, TimeLoc, TimeTimeLoc, TimeCmd, StartNew, TimeCmdHeadCode, FinRemHere,
15956  FNSNonRepeatToShuttle, SNTShuttle, SNSShuttle, SNSNonRepeatFromShuttle, FSHNewService, Repeat, PassTime,
15957  ExitRailway};
15958  enum TTimetableSequenceType {NoSequence, Start, Finish, Intermediate, SequTypeForRepeatEntry};
15959  enum TTimetableLocationType {NoLocation, AtLocation, EnRoute, LocTypeForRepeatEntry};
15960  enum TTimetableShuttleLinkType {NoShuttleLink, NotAShuttleLink, ShuttleLink, ShuttleLinkTypeForRepeatEntry};
15961 */
15962  if(ActionVectorEntry.SequenceType == Start)
15963  {
15964  if(ActionVectorEntry.FormatType == StartNew)
15965  {
15966  if(ActionVectorEntry.LocationName != "")
15967  {
15968  if(Track->TrackElementAt(742, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15969  {
15970  PartStr = "Enters at " + ActionVectorEntry.LocationName;
15971  }
15972  else
15973  {
15974  PartStr = "Created at " + ActionVectorEntry.LocationName;
15975  }
15976  }
15977  else // may be a named continuation or other element, and if so report that
15978  {
15979  AnsiString LocName = Track->TrackElementAt(739, ActionVectorEntry.RearStartOrRepeatMins).ActiveTrackElementName;
15980  if(Track->TrackElementAt(740, ActionVectorEntry.RearStartOrRepeatMins).TrackType == Continuation)
15981  {
15982  if(LocName != "")
15983  {
15984  PartStr = "Enters at " + LocName;
15985  }
15986  else // use rear position if it's a continuation
15987  {
15988  PartStr = "Enters at " + Track->TrackElementAt(737, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
15989  }
15990  }
15991  else // not a continuation
15992  {
15993  if(LocName != "")
15994  // if not a continuation then LocName should be same as ActionVectorEntry.LocationName
15995  // but include anyway
15996  {
15997  PartStr = "Created at " + LocName;
15998  }
15999  else // use rear position again
16000  {
16001  PartStr = "Created at " + Track->TrackElementAt(741, ActionVectorEntry.RearStartOrRepeatMins).ElementID;
16002  }
16003  }
16004  }
16005  TimeStr = Utilities->Format96HHMM(GetRepeatTime(20, ActionVectorEntry.EventTime, y, IncMinutes));
16006  }
16007  else if(ActionVectorEntry.FormatType == SNTShuttle)
16008  {
16009  if(y == 0) // first train
16010  {
16011  PartStr = "Enters at " + ActionVectorEntry.LocationName;
16012  TimeStr = Utilities->Format96HHMM(GetRepeatTime(21, ActionVectorEntry.EventTime, y, IncMinutes));
16013  }
16014  else
16015  {
16016  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16017  TimeStr = GetRepeatHeadCode(45, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16018  Utilities->Format96HHMM(GetRepeatTime(26, ActionVectorEntry.EventTime, y, IncMinutes));
16019  } // y-1 for headcode above since it is the last repeat value that the train is from
16020 
16021  }
16022  else if(ActionVectorEntry.Command == "Sfs")
16023  {
16024  PartStr = "New service at " + ActionVectorEntry.LocationName + " split from";
16025  TimeStr = GetRepeatHeadCode(33, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16026  Utilities->Format96HHMM(GetRepeatTime(24, ActionVectorEntry.EventTime, y, IncMinutes));
16027  }
16028  else if(ActionVectorEntry.Command == "Sns")
16029  {
16030  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16031  TimeStr = GetRepeatHeadCode(34, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16032  Utilities->Format96HHMM(GetRepeatTime(25, ActionVectorEntry.EventTime, y, IncMinutes));
16033  }
16034  else if(ActionVectorEntry.FormatType == SNSShuttle)
16035  {
16036  if(y == 0) // first entry from shuttle
16037  {
16038  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16039  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " +
16040  Utilities->Format96HHMM(GetRepeatTime(27, ActionVectorEntry.EventTime, y, IncMinutes));
16041  }
16042  else
16043  {
16044  PartStr = "Repeat shuttle service at " + ActionVectorEntry.LocationName + " from ";
16045  TimeStr = GetRepeatHeadCode(35, ActionVectorEntry.OtherHeadCode, y - 1, IncDigits) + " at " +
16046  Utilities->Format96HHMM(GetRepeatTime(22, ActionVectorEntry.EventTime, y, IncMinutes));
16047  } // y-1 for headcode above since it is the last repeat value that the train is from
16048 
16049  }
16050  else if(ActionVectorEntry.FormatType == SNSNonRepeatFromShuttle)
16051  {
16052  PartStr = "New service at " + ActionVectorEntry.LocationName + " from";
16053  // need repeat for the non-repeating headcode as it's the last train of the repeating shuttle
16054  TTrainDataEntry *TDE = ActionVectorEntry.LinkedTrainEntryPtr;
16055  AnsiString FirstHeadCode = TDE->HeadCode;
16056  int LastRepeatNumber = TDE->NumberOfTrains - 1;
16057  // a shuttle has to have at least 1 repeat
16058  int IncrementalDigits = TDE->ActionVector.at(TDE->ActionVector.size() - 1).FrontStartOrRepeatDigits;
16059  TimeStr = GetRepeatHeadCode(36, FirstHeadCode, LastRepeatNumber, IncrementalDigits) + " at " +
16060  Utilities->Format96HHMM(GetRepeatTime(23, ActionVectorEntry.EventTime, y, IncMinutes));
16061  }
16062  }
16063  else if(ActionVectorEntry.SequenceType == Intermediate)
16064  {
16065  if(ActionVectorEntry.FormatType == TimeTimeLoc)
16066  {
16067  // here need 2 entries if times different so push the first right away & the second later
16068  // if times same just give the arrival entry
16069  if(ActionVectorEntry.DepartureTime != ActionVectorEntry.ArrivalTime)
16070  {
16071  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16072  TimeStr = Utilities->Format96HHMM(GetRepeatTime(4, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16073  OneTTEntry.Action = PartStr;
16074  OneTTEntry.Time = TimeStr;
16075  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16076  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16077  TimeStr = Utilities->Format96HHMM(GetRepeatTime(5, ActionVectorEntry.DepartureTime, y, IncMinutes));
16078  }
16079  else
16080  {
16081  PartStr = "Arrives & departs " + ActionVectorEntry.LocationName;
16082  TimeStr = Utilities->Format96HHMM(GetRepeatTime(29, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16083  }
16084  }
16085  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime != TDateTime(-1)))
16086  {
16087  PartStr = "Arrives at " + ActionVectorEntry.LocationName;
16088  TimeStr = Utilities->Format96HHMM(GetRepeatTime(6, ActionVectorEntry.ArrivalTime, y, IncMinutes));
16089  }
16090  else if((ActionVectorEntry.FormatType == TimeLoc) && (ActionVectorEntry.ArrivalTime == TDateTime(-1)))
16091  {
16092  PartStr = "Departs from " + ActionVectorEntry.LocationName;
16093  TimeStr = Utilities->Format96HHMM(GetRepeatTime(7, ActionVectorEntry.DepartureTime, y, IncMinutes));
16094  }
16095  else if(ActionVectorEntry.FormatType == PassTime)
16096  {
16097  PartStr = "Passes " + ActionVectorEntry.LocationName;
16098  TimeStr = Utilities->Format96HHMM(GetRepeatTime(8, ActionVectorEntry.EventTime, y, IncMinutes));
16099  }
16100  else if(ActionVectorEntry.Command == "jbo")
16101  {
16102  PartStr = "Joined at " + ActionVectorEntry.LocationName + " by";
16103  TimeStr = GetRepeatHeadCode(37, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16104  Utilities->Format96HHMM(GetRepeatTime(9, ActionVectorEntry.EventTime, y, IncMinutes));
16105  }
16106  else if(ActionVectorEntry.Command == "fsp")
16107  {
16108  PartStr = "Splits from front at " + ActionVectorEntry.LocationName + " to form";
16109  TimeStr = GetRepeatHeadCode(38, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16110  Utilities->Format96HHMM(GetRepeatTime(10, ActionVectorEntry.EventTime, y, IncMinutes));
16111  }
16112  else if(ActionVectorEntry.Command == "rsp")
16113  {
16114  PartStr = "Splits from rear at " + ActionVectorEntry.LocationName + " to form";
16115  TimeStr = GetRepeatHeadCode(39, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16116  Utilities->Format96HHMM(GetRepeatTime(11, ActionVectorEntry.EventTime, y, IncMinutes));
16117  }
16118  else if(ActionVectorEntry.Command == "cdt")
16119  {
16120  PartStr = "Changes direction at " + ActionVectorEntry.LocationName;
16121  TimeStr = Utilities->Format96HHMM(GetRepeatTime(12, ActionVectorEntry.EventTime, y, IncMinutes));
16122  }
16123  }
16124  else if(ActionVectorEntry.SequenceType == Finish)
16125  {
16126  if(ActionVectorEntry.Command == "Fns")
16127  {
16128  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16129  TimeStr = GetRepeatHeadCode(40, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16130  Utilities->Format96HHMM(GetRepeatTime(13, ActionVectorEntry.EventTime, y, IncMinutes));
16131  }
16132  else if(ActionVectorEntry.Command == "F-nshs")
16133  {
16134  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16135  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16136  (GetRepeatTime(17, ActionVectorEntry.EventTime, y, IncMinutes));
16137  }
16138  else if((ActionVectorEntry.Command == "Fns-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16139  {
16140  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service ";
16141  TimeStr = GetRepeatHeadCode(41, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16142  Utilities->Format96HHMM(GetRepeatTime(14, ActionVectorEntry.EventTime, y, IncMinutes));
16143  // y+1 because it's the NEXT service repeat number that is relevant
16144  }
16145  else if((ActionVectorEntry.Command == "Fns-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16146  {
16147  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16148  TimeStr = ActionVectorEntry.NonRepeatingShuttleLinkHeadCode + " at " + Utilities->Format96HHMM
16149  (GetRepeatTime(15, ActionVectorEntry.EventTime, y, IncMinutes));
16150  }
16151  else if((ActionVectorEntry.Command == "Frh-sh") && (y < (TrainDataEntry.NumberOfTrains - 1)))
16152  {
16153  PartStr = "At " + ActionVectorEntry.LocationName + " forms new service";
16154  TimeStr = GetRepeatHeadCode(43, ActionVectorEntry.OtherHeadCode, y + 1, IncDigits) + " at " +
16155  Utilities->Format96HHMM(GetRepeatTime(16, ActionVectorEntry.EventTime, y, IncMinutes));
16156  // y+1 because it's the NEXT service repeat number that is relevant
16157  }
16158  else if((ActionVectorEntry.Command == "Frh-sh") && (y >= (TrainDataEntry.NumberOfTrains - 1)))
16159  {
16160  PartStr = "Terminates shuttle service at " + ActionVectorEntry.LocationName;
16161  // only used in chronological tt
16162  TimeStr = "End at " + Utilities->Format96HHMM(GetRepeatTime(28, ActionVectorEntry.EventTime, y, IncMinutes));
16163  // the "End at " is stripped out of the chronological tt but displayed in the traditional tt
16164  }
16165  else if(ActionVectorEntry.Command == "Frh")
16166  {
16167  PartStr = "Terminates at " + ActionVectorEntry.LocationName;
16168  // need here to examine the time of the preceding entry, may be ArrivalTime if TimeLoc, or EventTime otherwise
16169  if(z > 0)
16170  // should be for finish entry but include check for safety
16171  {
16172  if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
16173  {
16174  TimeStr = Utilities->Format96HHMM(GetRepeatTime(30, ActionVector.at(z - 1).EventTime, y, IncMinutes));
16175  }
16176  else if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
16177  {
16178  TimeStr = Utilities->Format96HHMM(GetRepeatTime(31, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
16179  }
16180  else
16181  {
16182  TimeStr = " "; // shouldn't ever get here
16183  }
16184  }
16185  }
16186  else if(ActionVectorEntry.Command == "Fer")
16187  {
16188  AnsiString AllowedExits;
16189  PartStr = "Exits railway" + GetExitLocationAndAt(0, ActionVectorEntry.ExitList, AllowedExits) + AllowedExits;
16190  TimeStr = Utilities->Format96HHMM(GetRepeatTime(18, ActionVectorEntry.EventTime, y, IncMinutes));
16191  }
16192  else if(ActionVectorEntry.Command == "Fjo")
16193  {
16194  PartStr = "At " + ActionVectorEntry.LocationName + " joins";
16195  TimeStr = GetRepeatHeadCode(44, ActionVectorEntry.OtherHeadCode, y, IncDigits) + " at " +
16196  Utilities->Format96HHMM(GetRepeatTime(19, ActionVectorEntry.EventTime, y, IncMinutes));
16197  }
16198  }
16199  else if(ActionVectorEntry.SequenceType == SequTypeForRepeatEntry)
16200  {
16201  continue; // no entry needed for a repeat
16202  }
16203  OneTTEntry.Action = PartStr;
16204  OneTTEntry.Time = TimeStr;
16205  OneTTTrain.OneFormattedTrainVector.push_back(OneTTEntry);
16206  // one per action
16207  }
16208  OneTTLine.OneCompleteFormattedTrainVector.push_back(OneTTTrain);
16209  // one per repeat
16210  }
16211  AllTTTrains->push_back(OneTTLine); // one per repeating train
16212  }
16213  // AllTTTrains vector now complete
16214 
16215  std::ofstream TTFile(TTFileName.c_str()); //formatted timetable
16216 
16217  if(TTFile == 0)
16218  {
16219  StopTTClockMessage(64, "Formatted timetable file failed to open - can't be created");
16220  delete AllTTTrains;
16221  Utilities->CallLogPop(1567);
16222  return;
16223  }
16224 /* formatted timetable types
16225  class TOneTrainFormattedEntry
16226  {
16227  AnsiString Action;//includes location if relevant
16228  AnsiString Time;
16229  };
16230 
16231  typedef std::vector<TOneTrainFormattedEntry> TOneFormattedTrainVector;
16232 
16233  class TOneCompleteFormattedTrain//headcode + list of actions
16234  {
16235  public:
16236  AnsiString HeadCode;
16237  TOneFormattedTrainVector OneFormattedTrainVector;
16238  };
16239 
16240  typedef std::vector<TOneCompleteFormattedTrain> TOneCompleteFormattedTrainVector;//list af all repeats
16241 
16242  class TTrainFormattedInformation//contains all information for a single TT entry (including repeats)
16243  {
16244  public:
16245  AnsiString Header;//description, mass, power, brake rate etc
16246  int NumberOfTrains;// number of repeats + 1
16247  TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector;//list af all repeats
16248  };
16249 
16250  typedef std::vector<TTrainFormattedInformation> TAllFormattedTrains;//all timetable in formatted form
16251  //end of formatted timetable types
16252 */
16253 
16254  // new layout using multiple rows
16255  TTFile << TableTitle.c_str() << '\n' << '\n';
16256  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16257  {
16258  TTFile << AllTTTrains->at(x).Header.c_str();
16259  TTFile << '\n';
16260  TTFile << ','; // for the blank line above the action list
16261  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16262  {
16263  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16264  {
16265  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str() << ',';
16266  }
16267  else
16268  {
16269  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode.c_str();
16270  }
16271  }
16272  TTFile << '\n' << '\n';
16273 
16274  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.size(); z++)
16275  {
16276  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(0).OneFormattedTrainVector.at(z).Action.c_str() << ',';
16277  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16278  {
16279  if(y < (AllTTTrains->at(x).NumberOfTrains - 1))
16280  {
16281  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str() << ',';
16282  }
16283  else
16284  {
16285  TTFile << AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time.c_str();
16286  }
16287  }
16288  TTFile << '\n';
16289  }
16290  TTFile << '\n' << '\n';
16291  }
16292 
16293  TTFile.close();
16294 
16295  AnsiString TTFileName2 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
16296 
16297  TTFileName2 = CurDir + "\\Formatted timetables\\Timetable " + TTFileName2 + "; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16298 
16299  std::ofstream TTFile2(TTFileName2.c_str()); //chronological timetable
16300 
16301  if(TTFile2 == 0)
16302  {
16303  StopTTClockMessage(67, "Chronological timetable file failed to open - can't be created");
16304  delete AllTTTrains;
16305  Utilities->CallLogPop(1710);
16306  return;
16307  }
16308  typedef std::multimap<AnsiString, AnsiString>TAnsiMultiMap;
16309  std::multimap<AnsiString, AnsiString>::iterator AMMIT;
16310  std::pair<AnsiString, AnsiString>AnsiMultiMapEntry;
16311 
16312  TAnsiMultiMap *TAMM = new TAnsiMultiMap;
16313  LastTTTime = ""; //records the very last time in the timetable - used in analysis file for Frh entries
16314 
16315  // multimap of AnsiStrings with TimeString as key (to sort automatically)
16316 
16317  TTFile2 << TableTitle.c_str() << '\n' << '\n';
16318  for(unsigned int x = 0; x < AllTTTrains->size(); x++)
16319  {
16320  for(int y = 0; y < AllTTTrains->at(x).NumberOfTrains; y++) // number of repeating trains
16321  {
16322  for(unsigned int z = 0; z < AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.size(); z++)
16323  {
16324  bool GiveMessagesFalse = false;
16325  AnsiString TimeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Time;
16326  AnsiString HeadCodeString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).HeadCode;
16327  AnsiString ActionString = AllTTTrains->at(x).OneCompleteFormattedTrainVector.at(y).OneFormattedTrainVector.at(z).Action;
16328  if(CheckHeadCodeValidity(11, GiveMessagesFalse, TimeString.SubString(1, 4)))
16329  // 'NXNN at HH:MM' (will return true if H/C as integ check passed)
16330  {
16331  // fails for HH:MM because of ':' or 'End at HH:MM' because of ' '
16332  AnsiString OtherHeadCode = TimeString.SubString(1, 4);
16333  TimeString = TimeString.SubString(9, 5);
16334  ActionString += " " + OtherHeadCode;
16335  }
16336  if(TimeString.SubString(1, 7) == "End at ")
16337  // for Frh-sh final entry
16338  {
16339  TimeString = TimeString.SubString(8, 5);
16340  }
16341  AnsiString OneLine = TimeString + ' ' + HeadCodeString + ' ' + ActionString + '\n';
16342  AnsiMultiMapEntry.first = TimeString;
16343  AnsiMultiMapEntry.second = OneLine;
16344  TAMM->insert(AnsiMultiMapEntry);
16345  }
16346  }
16347  }
16348 
16349  for(AMMIT = TAMM->begin(); AMMIT != TAMM->end(); AMMIT++)
16350  {
16351  TTFile2 << (AMMIT->second).c_str();
16352  }
16353  delete AllTTTrains;
16354  delete TAMM;
16355  TTFile2.close();
16356  Utilities->CallLogPop(1580);
16357 }
16358 
16359 // ---------------------------------------------------------------------------
16360 
16361 bool TTrainController::CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked,
16362  bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
16363 {
16364 
16365  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CreateTTAnalysisFile");
16366  bool AnalysisError = false;
16367  AnsiString SequenceLog = "SequenceLog\n";
16368 
16369 /* Double crosslink (shuttle) table:
16370 
16371 Command Format OtherHead NonRepeating- LinkTrain- NonRepeating- Decsription
16372  Code ShuttleLink- EntryPtr ShuttleLink-
16373  HeadCode EntryPtr
16374 
16375 Snt-sh SNTShuttle Y (rtn shuttle) N Y (rtn sh) N Simple shuttle - no feeder service
16376 Frh-sh TimeCmdHeadCode Y (outwd shuttle) N Y (outwd sh) N Simple shuttle - no finishing service
16377 F-nshs FNSNonRepeatToShuttle N (shld be Y for outwd shuttle) Y (shld be N) Y (correct) N (correct) Feeder service link to shuttle
16378 Sns-sh SNSShuttle Y (rtn shuttle) Y (feeder) Y (rtn) Y (fdr) Shuttle link from feeder service
16379 Sns-fsh SNSNonRepeatFromShuttle N (shld be Y for rtn shuttle) Y (shld be N) Y (correct) N (correct) Finishing service link from shuttle
16380 Fns-sh FSHNewService Y (outwd shuttle) Y (finishing) Y (outwd sh) Y (finish) Shuttle link to finishing service
16381 
16382 Note: Any shuttle start can have any finish - feeder and finish, neither, feeder but no finish & vice versa.
16383 */
16384 
16385  try
16386  {
16387  //New section at v2.5.0 for tt conflict analysis
16388  /*
16389  typedef std::list<AnsiString> TServiceCallingLocsList;
16390  typedef std::map<AnsiString, TServiceCallingLocsList> TAllServiceCallingLocsMap;
16391 
16393  struct TLocServiceTimes
16394  {
16395  AnsiString Location;
16396  AnsiString ServiceAndRepeatNum;
16397  AnsiString AtLocTime;
16398  AnsiString ArrTime;
16399  AnsiString DepTime;
16400  AnsiString FrhMarker;
16401  };
16402  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16403  */
16404 
16405  //first have to check through all the services and give each one a unique name, or the analysis won't recognise differences between services that have the same reference
16406  //to do that need a new TrainDataVector as don't want to change anything in the original. TrainDataVectorCopy is used for building AllServiceCallingLocsMap & LocServiceTimesVector
16407 
16408  TTrainDataVector TrainDataVectorCopy = TrainDataVector; //don't need it on heap as TrainController is on the heap. Didn't need others in CreatFormattedTimetables but leave as is.
16409  TTrainDataVector::iterator TDVIt, TDVCopyIt;
16410  int Suffix = 0;
16411  int IteratorNumber = 0;
16412  AnsiString AnsiSuffix = "";
16413  for(TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end() - 1; TDVIt++)
16414  {
16415  IteratorNumber++; //first value in loop is 1
16416  Suffix = 0;
16417  for(TDVCopyIt = TrainDataVectorCopy.begin() + IteratorNumber; TDVCopyIt != TrainDataVectorCopy.end(); TDVCopyIt++)
16418  {
16419  if(TDVCopyIt->ServiceReference == TDVIt->ServiceReference)
16420  {
16421  Suffix++; //first value is 1
16422  AnsiSuffix = AnsiString(Suffix);
16423  TDVCopyIt->ServiceReference = TDVIt->ServiceReference + "/" + AnsiSuffix;
16424  }
16425  }
16426  }
16427  SequenceLog += "1\n";
16428  //build AllServiceCallingLocsMap, it only uses the base service reference (with /1, /2 etc suffixes) as later times are calculated from the repeat number
16429  TServiceCallingLocsList ServiceCallingLocsList;
16430  std::pair<AnsiString, TServiceCallingLocsList> AllServiceCallingLocsEntry;
16431  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16432  {
16433  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16434  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16435  AllServiceCallingLocsEntry.first = TrainDataEntry.ServiceReference;
16436  ServiceCallingLocsList.clear();
16437  if(ActionVector.empty())
16438  {
16439  continue;
16440  }
16441  if(ActionVector.at(0).SignallerControl)
16442  {
16443  continue;
16444  }
16445  for(unsigned int z = 0; z < ActionVector.size(); z++)
16446  {
16447  TActionVectorEntry AVE = ActionVector.at(z);
16448  if(AVE.FormatType == StartNew)
16449  {
16450  if(AVE.LocationType == AtLocation) //located Snt
16451  {
16452  ServiceCallingLocsList.push_back(AVE.LocationName);
16453  }
16454  else //unlocated Snt (could be entering at continuation)
16455  {
16457  if(TE.ActiveTrackElementName != "")
16458  {
16459  ServiceCallingLocsList.push_back(TE.ActiveTrackElementName);
16460  }
16461  else
16462  {
16463  int HLoc = TE.HLoc;
16464  int VLoc = TE.VLoc;
16465  AnsiString HString;
16466  AnsiString VString;
16467  if(HLoc < 0)
16468  {
16469  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16470  }
16471  else
16472  {
16473  HString = AnsiString(HLoc);
16474  }
16475  if(VLoc < 0)
16476  {
16477  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16478  }
16479  else
16480  {
16481  VString = AnsiString(VLoc);
16482  }
16483  ServiceCallingLocsList.push_back(HString + '-' + VString);
16484  }
16485  }
16486  }
16487  else if(AVE.SequenceType == Start) //other start entries, all located
16488  {
16489  ServiceCallingLocsList.push_back(AVE.LocationName);
16490  }
16491  else if(AVE.FormatType == TimeLoc) //z must be > 0
16492  {
16493  if(ServiceCallingLocsList.back() != AVE.LocationName)
16494  {
16495  ServiceCallingLocsList.push_back(AVE.LocationName); //may be listed twice in succession so only want one entry
16496  }
16497  }
16498  else if(AVE.FormatType == PassTime)
16499  {
16500  ServiceCallingLocsList.push_back(AVE.LocationName);
16501  }
16502  else if(AVE.FormatType == TimeTimeLoc)
16503  {
16504  ServiceCallingLocsList.push_back(AVE.LocationName);
16505  }
16506  else if(AVE.Command == "cdt") //list if not next to start or finish
16507  {
16508  if(ActionVector.at(z-1).SequenceType == Start)
16509  {
16510  continue;
16511  }
16512  else if(ActionVector.at(z+1).SequenceType == Finish) //although deal with Fer entries cdt (train stopped) can't precede FER (train moving)
16513  {
16514  continue;
16515  }
16516  else
16517  {
16518  AnsiString TimeString = Utilities->Format96HHMM(AVE.EventTime);
16519  ServiceCallingLocsList.push_back("%%%" + TimeString); //%%% is a marker - unlikely that any locations will begin with this & easy to check to identify a time
16520  }
16521  }
16522  else if(AVE.FormatType == ExitRailway) //Fer
16523  {
16524  TTrackElement TE = Track->TrackElementAt(995, AVE.ExitList.front());
16525  AnsiString LName = TE.ActiveTrackElementName;
16526  if(LName != "")
16527  {
16528  ServiceCallingLocsList.push_back(LName);
16529  }
16530  else
16531  {
16532  int HLoc = TE.HLoc;
16533  int VLoc = TE.VLoc;
16534  AnsiString HString;
16535  AnsiString VString;
16536  if(HLoc < 0)
16537  {
16538  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16539  }
16540  else
16541  {
16542  HString = AnsiString(HLoc);
16543  }
16544  if(VLoc < 0)
16545  {
16546  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16547  }
16548  else
16549  {
16550  VString = AnsiString(VLoc);
16551  }
16552  ServiceCallingLocsList.push_back(HString + '-' + VString);
16553  }
16554  }
16555  }
16556  AllServiceCallingLocsEntry.second = ServiceCallingLocsList;
16557  AllServiceCallingLocsMap.insert(AllServiceCallingLocsEntry);
16558  }
16559  //AllServiceCallingLocsMap built
16560  SequenceLog += "2\n";
16561  //test validity of AllServiceCallingLocsMap
16562 /*
16563  AnsiString TestFile = CurDir + "\\Formatted timetables\\TestFile; " + RailwayTitle + "; " + TimetableTitle + ".txt";
16564  std::ofstream Test(TestFile.c_str());
16565 
16566  if(TestFile == 0)
16567  {
16568  ShowMessage("TestFile failed to open - can't be created");
16569  Utilities->CallLogPop();
16570  return false;
16571  }
16572 
16573  for(TAllServiceCallingLocsMap::iterator ASCLIt = AllServiceCallingLocsMap.begin(); ASCLIt != AllServiceCallingLocsMap.end(); ASCLIt++)
16574  {
16575  Test << ASCLIt->first << '\n'; //service ref
16576  for(TServiceCallingLocsList::iterator SCLIt = ASCLIt->second.begin(); SCLIt != ASCLIt->second.end(); SCLIt++)
16577  {
16578  Test << *SCLIt << '\n';
16579  }
16580  Test << "\n\n";
16581  }
16582  Test.close();
16583  Utilities->CallLogPop();
16584  return true;
16585 */
16586 
16587  //initialise variables before calc LastTTTime & build LocServiceTimesVector
16588  if(TrainDataVector.empty())
16589  {
16590  ShowMessage("Unable to create a program-readable timetable - please check the timetable file validity");
16591  Utilities->CallLogPop(2209);
16592  return(false);
16593  }
16594  TLocServiceTimes TLSTEntry;
16595  TLocServiceTimesVector LocServiceTimesVector; //will be on heap as TrainController is on the heap
16596  bool NumPlatsAtThisLocCalculated = false, ArrivalsPrinted = false, DeparturesPrinted = false, AtLocsPrinted = false;
16597  AnsiString PreviousService = "", PreviousServiceAndRepeatNumTotalOutput = "", BasicTime = "", MinuteString = "", LastAnsiTime = "";
16598  int NumTrains = 0, NumPlats = 0, LastFrhCount = 0, FrhCount = 0, NumTrainsAtLoc = 0;
16599  LastTTTime = "";
16600  SequenceLog += "3\n";
16601  //calculate LastTTTime
16602  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16603  {
16604  TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16605  TActionVector &ActionVector = TrainDataEntry.ActionVector;
16606  TActionVectorIterator AVLast = ActionVector.end() - 1; //points to last entry
16607  TDateTime LastTDTime;
16608  int IncMinutes = 0;
16609  NumTrains = TrainDataEntry.NumberOfTrains;
16610  if(ActionVector.empty())
16611  {
16612  continue;
16613  }
16614  if(ActionVector.at(0).SignallerControl)
16615  {
16616  continue;
16617  }
16618  if(AVLast->FormatType == Repeat)
16619  {
16620  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16621  AVLast--; //now points to the command before the repeat
16622  }
16623  if(AVLast->FormatType == FinRemHere) //not 'else if' as may have both a repeat and an Frh
16624  {
16625  AVLast--; //points to last timed entry
16626  }
16627  //here AVLast points to last entry with a time
16628  if(AVLast->ArrivalTime != TDateTime(-1))
16629  {
16630  LastTDTime = AVLast->ArrivalTime;
16631  }
16632  else if(AVLast->EventTime != TDateTime(-1)) //can't be a departure time
16633  {
16634  LastTDTime = AVLast->EventTime;
16635  }
16636  else
16637  {
16638  continue; //shouldn't ever reach here but if do then skip this service
16639  }
16640  if(NumTrains == 1)
16641  {
16642  LastAnsiTime = Utilities->Format96HHMM(LastTDTime);
16643  }
16644  else
16645  {
16646  LastAnsiTime = Utilities->Format96HHMM(GetRepeatTime(59, LastTDTime, NumTrains - 1, IncMinutes));
16647  }
16648  if(LastAnsiTime > LastTTTime)
16649  {
16650  LastTTTime = LastAnsiTime;
16651  }
16652  }
16653  SequenceLog += "4\n";
16654 //build LocServiceTimesVector
16655 
16656 /*
16657  struct TLocServiceTimes
16658  {
16659  AnsiString Location;
16660  AnsiString ServiceAndRepeatNum;
16661  AnsiString AtLocTime;
16662  AnsiString ArrTime;
16663  AnsiString DepTime;
16664  AnsiString FrhMarker;
16665  };
16666  typedef std::vector<TLocServiceTimes> TLocServiceTimesVector;
16667 
16668 This works as follows:
16669 ServiceAndRepeatNum is taken from the TrainDataVector as it is the same for all actionvector entries
16670 Location is taken from ActionVectorEntry.LocationName if there is one, or from the H & V locations if not (e.g. at an unnamed Fer)
16671 AtLocTime is always entered either on its own or with ArrTime or DepTime as appropriate
16672 
16673 Every action for every train is examined and times entered as follows:-
16674 a) a located Snt: entry time becomes the AtLocTime, and all subsequent minutes entered too up to but not including a departure or a finish
16675 b) an unlocated Snt: entry time becomes DepTime
16676 c) all other start entries: entry time becomes AtLoc, and all subsequent minutes entered too up to but not including a departure or a finish
16677 d) TimeLoc Arr: entry time becomes ArrTime, and all subsequent minutes entered too up to but not including a departure or a finish
16678 e) TimeLoc Dep: entry time becomes DepTime, checks if DepTime same as earlier ArrTime and if so all times go in as one entry
16679 f) TimeTimeLoc: Arrival time entered as ArrTime, a check if Arr & Dep same and if so go in as one entry, else all minutes between entered as AtLocs then DepTime
16680 g) ExitRailway (Fer): check if located and use LocationName if so. else use H & V positions, time becomes AtLocTime
16681 h) Frh: use the earlier vector time as the AtLocTime and set FrhMarker, and enter all minutes to end of timetable as AtLocs
16682 i) Frh-sh: for the last train use time as AtLocTime, set FrhMarker, and enter all minutes to end of timetable as AtLocs
16683 j) all other finish entries (all link to another service) are ignored as will be listed for the linked service
16684 */
16685  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
16686  {
16687  const TTrainDataEntry &TrainDataEntry = TrainDataVectorCopy.at(x);
16688  const TActionVector &ActionVector = TrainDataEntry.ActionVector;
16689  AnsiString ServiceRef = TrainDataEntry.ServiceReference;
16690  int IncMinutes = 0;
16691  NumTrains = TrainDataEntry.NumberOfTrains;
16692  if(ActionVector.empty())
16693  {
16694  continue;
16695  }
16696  if(ActionVector.at(0).SignallerControl)
16697  {
16698  continue;
16699  }
16700  if(ActionVector.at(ActionVector.size() - 1).FormatType == Repeat)
16701  {
16702  IncMinutes = ActionVector.at(ActionVector.size() - 1).RearStartOrRepeatMins;
16703  }
16704  for(int y = 0; y < NumTrains; y++) //y is the repeat number
16705  {
16706  if(NumTrains == 1)
16707  {
16708  TLSTEntry.ServiceAndRepeatNum = ServiceRef;
16709  }
16710  else if(y == 0)
16711  {
16712  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (First service)";
16713  }
16714  else
16715  {
16716  TLSTEntry.ServiceAndRepeatNum = ServiceRef + " (Repeat " + AnsiString(y) + ")";
16717  }
16718  for(unsigned int z = 0; z < ActionVector.size(); z++)
16719  {
16720  TActionVectorEntry AVE = ActionVector.at(z);
16721  TLSTEntry.AtLocTime = "";
16722  TLSTEntry.ArrTime = "";
16723  TLSTEntry.DepTime = "";
16724  TLSTEntry.Location = "";
16725  TLSTEntry.FrhMarker = "";
16726 
16727  if(AVE.FormatType == StartNew) //Snt only
16728  {
16729  if(AVE.LocationType == AtLocation) //located Snt, class time as AtLocTime
16730  {
16731  TLSTEntry.Location = AVE.LocationName;
16732  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(58, AVE.EventTime, y, IncMinutes));
16733  LocServiceTimesVector.push_back(TLSTEntry);
16734 
16735  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16736  AnsiString IncTime = "", FoundStopTime = ""; //these handled in later checks
16737  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16738  {
16739  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16740  {
16741  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(62, ActionVector.at(a).DepartureTime, y, IncMinutes));
16742  break;
16743  }
16744  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16745  {
16746  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(63, ActionVector.at(a).EventTime, y, IncMinutes));
16747  break;
16748  }
16749  }
16750  if(FoundStopTime == "")
16751  {
16752  throw Exception("Failure to determine FoundStopTime for located Snt");
16753  }
16754  int WhileCount = 0;
16755  while(true)
16756  {
16757  //add minutes until reach FoundStopTime but don't add that time
16758  WhileCount++;
16759  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16760  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16761  TLSTEntry.DepTime = "";
16762  TLSTEntry.ArrTime = "";
16763  if(IncTime >= FoundStopTime) //don't add that time
16764  {
16765  break;
16766  }
16767  LocServiceTimesVector.push_back(TLSTEntry);
16768  if(WhileCount > 2000)
16769  {
16770  throw Exception("While loop failed to break in 2000 loops for located Snt");
16771  }
16772  }
16773  }
16774  else //unlocated Snt, use the EventTime as DepTime for this vector
16775  {
16777  if(TE.ActiveTrackElementName != "")
16778  {
16779  TLSTEntry.Location = TE.ActiveTrackElementName;
16780  }
16781  else
16782  {
16783  int HLoc = TE.HLoc;
16784  int VLoc = TE.VLoc;
16785  AnsiString HString;
16786  AnsiString VString;
16787  if(HLoc < 0)
16788  {
16789  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
16790  }
16791  else
16792  {
16793  HString = AnsiString(HLoc);
16794  }
16795  if(VLoc < 0)
16796  {
16797  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
16798  }
16799  else
16800  {
16801  VString = AnsiString(VLoc);
16802  }
16803  TLSTEntry.Location = HString + '-' + VString;
16804  }
16805  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(49, AVE.EventTime, y, IncMinutes));
16806  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16807  LocServiceTimesVector.push_back(TLSTEntry);
16808  }
16809  }
16810 
16811  else if(AVE.SequenceType == Start) //other start entries, all located
16812  {
16813  TLSTEntry.Location = AVE.LocationName;
16814  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(50, AVE.EventTime, y, IncMinutes));
16815  LocServiceTimesVector.push_back(TLSTEntry);
16816  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16817  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16818  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16819  {
16820  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16821  {
16822  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(64, ActionVector.at(a).DepartureTime, y, IncMinutes));
16823  break;
16824  }
16825  if(ActionVector.at(a).SequenceType == Finish) //finish catered in a later test
16826  {
16827  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(65, ActionVector.at(a).EventTime, y, IncMinutes));
16828  break;
16829  }
16830  }
16831  if(FoundStopTime == "")
16832  {
16833  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16834  }
16835  int WhileCount = 0;
16836  while(true)
16837  {
16838  //add minutes until reach FoundStopTime but don't add that time
16839  WhileCount++;
16840  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16841  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16842  TLSTEntry.DepTime = "";
16843  TLSTEntry.ArrTime = "";
16844  if(IncTime >= FoundStopTime) //don't add that time
16845  {
16846  break;
16847  }
16848  LocServiceTimesVector.push_back(TLSTEntry);
16849  if(WhileCount > 2000)
16850  {
16851  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16852  }
16853  }
16854  }
16855 
16856  else if(AVE.FormatType == TimeLoc) //could be arr or dep, if arrival add in all mins to the departure or finish
16857  {
16858  TLSTEntry.Location = AVE.LocationName;
16859  if(AVE.ArrivalTime > TDateTime(-1)) //one or other set, not both, in this case arrival
16860  {
16861  bool SkipAddingMinutes = false;
16862  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(51, AVE.ArrivalTime, y, IncMinutes));
16863  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16864  LocServiceTimesVector.push_back(TLSTEntry); //Arr and AtLoc added (may be popped if dep time found to be same at next TimeLoc)
16865  //now look forwards until find a departure or a Fns, Fns-sh etc & add in all the minutes up to but not including the dep or finish times
16866  AnsiString IncTime = "", FoundStopTime = ""; //these handled in other checks
16867  for(unsigned int a = z + 1; a < ActionVector.size(); a++)
16868  {
16869  if(ActionVector.at(a).FormatType == TimeLoc) //must be a departure
16870  {
16871  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(66, ActionVector.at(a).DepartureTime, y, IncMinutes));
16872  break;
16873  }
16874  if(ActionVector.at(a).SequenceType == Finish) //finish catered for in a later test
16875  {
16876  FoundStopTime = Utilities->Format96HHMM(GetRepeatTime(67, ActionVector.at(a).EventTime, y, IncMinutes));
16877  if((a <= (z + 2)) && (FoundStopTime == TLSTEntry.ArrTime) && ((ActionVector.at(a).LinkedTrainEntryPtr > 0) || (ActionVector.at(a).NonRepeatingShuttleLinkEntryPtr > 0)))
16878  //finish immediately after arrival at same time, and a forward linked service. Added at v2.6.0 to prevent two linked trains being listed at same location
16879  //at v2.10.0 changed (a == z + 1) to (a <= (z + 2)) as can have a cdt between, this allows for that
16880  {
16881  LocServiceTimesVector.pop_back(); //pop the entry as the linked train will be listed at the relevant time and don't want to list both
16882  SkipAddingMinutes = true;
16883  }
16884  break;
16885  }
16886  }
16887  if(FoundStopTime == "")
16888  {
16889  throw Exception("Failure to determine FoundStopTime for SequenceType == Start");
16890  }
16891  if(!SkipAddingMinutes)
16892  {
16893  int WhileCount = 0;
16894  while(true)
16895  {
16896  //add minutes until reach FoundStopTime but don't add that time
16897  WhileCount++;
16898  IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16899  TLSTEntry.AtLocTime = IncTime; //all entered times will be AtLocs
16900  TLSTEntry.DepTime = "";
16901  TLSTEntry.ArrTime = "";
16902  if(IncTime >= FoundStopTime) //don't add that time
16903  {
16904  break;
16905  }
16906  LocServiceTimesVector.push_back(TLSTEntry);
16907  if(WhileCount > 2000)
16908  {
16909  throw Exception("While loop failed to break in 2000 loops for SequenceType == Start");
16910  }
16911  }
16912  }
16913  }
16914  else if(AVE.DepartureTime > TDateTime(-1)) //need to check if the arrival time (which should already be listed) is same and if so put all times on one line
16915  {
16916  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(52, AVE.DepartureTime, y, IncMinutes));
16917  TLSTEntry.AtLocTime = TLSTEntry.DepTime;
16918  if((TLSTEntry.Location == LocServiceTimesVector.back().Location) && (TLSTEntry.ServiceAndRepeatNum == LocServiceTimesVector.back().ServiceAndRepeatNum)) //if not it's a new service
16919  {
16920  if(TLSTEntry.DepTime == LocServiceTimesVector.back().ArrTime)
16921  {
16922  TLSTEntry.ArrTime = LocServiceTimesVector.back().ArrTime;
16923  LocServiceTimesVector.pop_back();
16924  LocServiceTimesVector.push_back(TLSTEntry); //Arr, Dep and AtLoc added in place of earlier Arr entry.
16925  }
16926  else //just add the dep & atloc times
16927  {
16928  TLSTEntry.ArrTime = "";
16929  LocServiceTimesVector.push_back(TLSTEntry);
16930  }
16931  }
16932  else //just add the dep & atloc times
16933  {
16934  TLSTEntry.ArrTime = "";
16935  LocServiceTimesVector.push_back(TLSTEntry);
16936  }
16937  }
16938  }
16939 
16940  else if(AVE.FormatType == TimeTimeLoc)
16941  {
16942  TLSTEntry.Location = AVE.LocationName;
16943  if(AVE.ArrivalTime > TDateTime(-1)) //should be
16944  {
16945  TLSTEntry.ArrTime = Utilities->Format96HHMM(GetRepeatTime(53, AVE.ArrivalTime, y, IncMinutes));
16946  TLSTEntry.AtLocTime = TLSTEntry.ArrTime;
16947  }
16948  if(AVE.DepartureTime > TDateTime(-1)) //should be
16949  {
16950  TLSTEntry.DepTime = Utilities->Format96HHMM(GetRepeatTime(54, AVE.DepartureTime, y, IncMinutes));
16951  }
16952  if(TLSTEntry.ArrTime == TLSTEntry.DepTime)
16953  {
16954  LocServiceTimesVector.push_back(TLSTEntry);
16955  }
16956  else
16957  {
16958  AnsiString TempDepTime = TLSTEntry.DepTime; //save it temporarily
16959  TLSTEntry.DepTime = "";
16960  LocServiceTimesVector.push_back(TLSTEntry); //push just the arrival and AtLoc times
16961  TLSTEntry.ArrTime = ""; //done with this now
16962  while(TLSTEntry.AtLocTime < TempDepTime)
16963  {
16964  TLSTEntry.AtLocTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
16965  if(TLSTEntry.AtLocTime == TempDepTime)
16966  {
16967  TLSTEntry.DepTime = TempDepTime; //restore value
16968  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc and Dep times - will finish loop after this
16969  }
16970  else
16971  {
16972  LocServiceTimesVector.push_back(TLSTEntry); //push the AtLoc time on its own
16973  }
16974  }
16975  }
16976  }
16977 
16978  else if(AVE.FormatType == PassTime) //added at v2.9.1
16979  { //adds 2 entries, 1st with PassTime as ArrTime and AtLocTime, 2nd with PassTime as AtLocTime & DepTime
16980  TLSTEntry.Location = AVE.LocationName;;
16981  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(73, AVE.EventTime, y, IncMinutes));
16982  TLSTEntry.ArrTime = TLSTEntry.AtLocTime; //DepTime already set to null
16983  LocServiceTimesVector.push_back(TLSTEntry); //1st entry
16984  TLSTEntry.ArrTime = ""; //need to reset this to null
16985  TLSTEntry.DepTime = TLSTEntry.AtLocTime;
16986  LocServiceTimesVector.push_back(TLSTEntry); //2nd entry
16987  }
16988 
16989  else if(AVE.FormatType == ExitRailway) //Fer
16990  {
16991  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(55, AVE.EventTime, y, IncMinutes));
16992  //don't know which exit will be used during operation so use the first in ExitList, if several with different names then will
16993  //be wrong, but can't guess from here & most will have same name
16994  AnsiString LName = Track->TrackElementAt(990, AVE.ExitList.front()).ActiveTrackElementName;
16995  if(LName != "")
16996  {
16997  TLSTEntry.Location = LName;
16998  }
16999  else
17000  {
17001  int HLoc = Track->TrackElementAt(991, AVE.ExitList.front()).HLoc;
17002  int VLoc = Track->TrackElementAt(992, AVE.ExitList.front()).VLoc;
17003  AnsiString HString;
17004  AnsiString VString;
17005  if(HLoc < 0)
17006  {
17007  HString = AnsiString('N') + AnsiString(HLoc).SubString(2, AnsiString(HLoc).Length() - 1); //strip off '-'
17008  }
17009  else
17010  {
17011  HString = AnsiString(HLoc);
17012  }
17013  if(VLoc < 0)
17014  {
17015  VString = AnsiString('N') + AnsiString(VLoc).SubString(2, AnsiString(VLoc).Length() - 1); //strip off '-'
17016  }
17017  else
17018  {
17019  VString = AnsiString(VLoc);
17020  }
17021  TLSTEntry.Location = HString + '-' + VString;
17022  }
17023  LocServiceTimesVector.push_back(TLSTEntry); //just use the exit time as AtLocTime
17024  }
17025 
17026  else if(AVE.FormatType == FinRemHere) //Frh, not Frh-sh, that dealt with next
17027  {
17028  AnsiString FrhTime;
17029  if(ActionVector.at(z - 1).ArrivalTime != TDateTime(-1))
17030  {
17031  FrhTime = Utilities->Format96HHMM(GetRepeatTime(56, ActionVector.at(z - 1).ArrivalTime, y, IncMinutes));
17032  }
17033  else if(ActionVector.at(z - 1).EventTime != TDateTime(-1))
17034  {
17035  FrhTime = Utilities->Format96HHMM(GetRepeatTime(57, ActionVector.at(z - 1).EventTime, y, IncMinutes));
17036  }
17037  TLSTEntry.AtLocTime = FrhTime; //use the last entry time as the first recorded time
17038  TLSTEntry.Location = AVE.LocationName;
17039  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17040  TLSTEntry.FrhMarker = "Frh";
17041  LocServiceTimesVector.push_back(TLSTEntry);
17042  TLSTEntry.FrhMarker = "";
17043  //add all times from next minute to end of timetable
17044  while(IncTime <= LastTTTime)
17045  {
17046  TLSTEntry.AtLocTime = IncTime;
17047  LocServiceTimesVector.push_back(TLSTEntry);
17048  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17049  }
17050  }
17051 
17052  else if(AVE.Command == "Frh-sh") //do nothing if links to other shuttle but treat as Frh when remaining here
17053  {
17054  if(y == NumTrains - 1) //last repeat, it remains here when accessed for the last train
17055  {
17056  TLSTEntry.AtLocTime = Utilities->Format96HHMM(GetRepeatTime(68, AVE.EventTime, y, IncMinutes));
17057  TLSTEntry.Location = AVE.LocationName;
17058  AnsiString IncTime = Utilities->IncrementAnsiTimeOneMinute(TLSTEntry.AtLocTime);
17059  TLSTEntry.FrhMarker = "Frh";
17060  LocServiceTimesVector.push_back(TLSTEntry);
17061  TLSTEntry.FrhMarker = "";
17062  //add all times from next minute to end of timetable
17063  while(IncTime <= LastTTTime)
17064  {
17065  TLSTEntry.AtLocTime = IncTime;
17066  LocServiceTimesVector.push_back(TLSTEntry);
17067  IncTime = Utilities->IncrementAnsiTimeOneMinute(IncTime);
17068  }
17069  }
17070  }
17071 
17072  else if(AVE.SequenceType == Finish) //other finish types - all located & all link to another service
17073  {
17074  //nothing is done here as the entry will be listed at this time under the new service reference
17075  }
17076  }
17077  }
17078  }
17079  SequenceLog += "5\n";
17080  //now sort in location order
17081  std::sort(LocServiceTimesVector.begin(), LocServiceTimesVector.end(), &LocServiceTimesLocationSort); //LocServiceTimesLocationSort is a function pointer
17082  //LocServiceTimesVector now complete & sorted in location order
17083 
17085 /*
17086  std::ofstream LSTVFile("LSTVFile.txt");
17087  for(TLocServiceTimesVector::iterator LSTVIt = LocServiceTimesVector.begin(); LSTVIt != LocServiceTimesVector.end(); LSTVIt++)
17088  {
17089  LSTVFile << LSTVIt->Location + '\n';
17090  LSTVFile << LSTVIt->ServiceAndRepeatNum + '\n';
17091  LSTVFile << "AtLocTime = " + LSTVIt->AtLocTime + '\n';
17092  LSTVFile << "ArrTime = " + LSTVIt->ArrTime + '\n';
17093  LSTVFile << "DepTime = " + LSTVIt->DepTime + '\n';
17094  if(LSTVIt->FrhMarker == "")
17095  {
17096  LSTVFile << "Not Frh\n";
17097  }
17098  else
17099  {
17100  LSTVFile << LSTVIt->FrhMarker + '\n';
17101  }
17102  LSTVFile << '\n';
17103  }
17104  LSTVFile.close();
17105 */
17106  //declare pointers for use in printouts
17107  TLocServiceTimesVector::iterator Ptr1, Ptr2;
17108 
17109  //set up the output file
17110  AnsiString TTFileName3 = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
17111  TTFileName3 = CurDir + "\\Formatted timetables\\Conflict Analysis " + TTFileName3 + "; " + RailwayTitle + "; " + TimetableTitle + ".csv";
17112 
17113  std::ofstream TTFile3(TTFileName3.c_str());
17114 
17115  if(TTFile3 == 0)
17116  {
17117  ShowMessage("Conflict Analysis file failed to open - can't be created");
17118  Utilities->CallLogPop(2210);
17119  return(false);
17120  }
17121  if(LocServiceTimesVector.empty())
17122  {
17123  ShowMessage("No timetabled services found");
17124  TTFile3.close();
17125  DeleteFile(TTFileName3);
17126  Utilities->CallLogPop(2211);
17127  return(false);
17128  }
17129  TTFile3 << "Timetable analysis for timetable: '" + TimetableTitle + "' in conjunction with railway: '" + RailwayTitle + "'\n";
17130  TTFile3 << "See user manual or on-screen help section 5.12 for detailed information.\n\n\n";
17131  SequenceLog += "6\n";
17132 
17133 /* print out TrainDataVectorCopy for debugging purposes
17134 std::ofstream TDVCFile("TDVCFile.txt");
17135 for(TTrainDataVector::iterator TDVCIt = TrainDataVectorCopy.begin(); TDVCIt != TrainDataVectorCopy.end(); TDVCIt++)
17136 {
17137  TDVCFile << TDVCIt->ServiceReference + '\n';
17138  TDVCFile << TDVCIt->Description + '\n';
17139  for(unsigned int x = 0; x < TDVCIt->ActionVector.size(); x++)
17140  {
17141  TActionVectorEntry AVE = TDVCIt->ActionVector.at(x);
17142  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
17143  {
17144  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
17145  }
17146  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
17147  {
17148  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
17149  }
17150  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
17151  {
17152  TDVCFile << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
17153  }
17154  else if(AVE.FormatType == TimeTimeLoc)
17155  {
17156  TDVCFile << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
17157  }
17158  else if(AVE.FormatType == PassTime)
17159  {
17160  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
17161  }
17162  else if(AVE.FormatType == ExitRailway)
17163  {
17164  TDVCFile << AnsiString(AVE.EventTime.TimeString()) << " Fer" << '\n';
17165  }
17166  else if(AVE.FormatType == FinRemHere)
17167  {
17168  TDVCFile << "Frh" << '\n';
17169  }
17170  }
17171  TDVCFile << '\n';
17172 }
17173 TDVCFile.close();
17174 
17175 */
17176  //arrivals
17177  if(ArrChecked)
17178  {
17179  //sort in ArrTime order for each location
17180  Ptr1 = LocServiceTimesVector.begin();
17181  Ptr2 = Ptr1 + 1;
17182  while(Ptr2 != LocServiceTimesVector.end())
17183  {
17184  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17185  {
17186  Ptr2++;
17187  if(Ptr2 == LocServiceTimesVector.end())
17188  {
17189  break;
17190  }
17191  }
17192  std::sort(Ptr1, Ptr2, &LocServiceTimesArrTimeSort);
17193  Ptr1 = Ptr2; //first entry with next name
17194  if(Ptr2 != LocServiceTimesVector.end())
17195  {
17196  Ptr2++;
17197  }
17198  }
17199 
17200  //routine for arrivals - number of trains arriving within the specified range with services listed at the end
17201 
17202  TTFile3 << "Arrival & pass analysis: an asterisk means that the number of same approach code arrivals and passes is equal to or greater than the number of platforms.\n";
17203  TTFile3 << "If the total number of arrivals and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17204  MinuteString = " minutes";
17205  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17206  if(ArrRange == 1)
17207  {
17208  MinuteString = " minute";
17209  }
17210  TTFile3 << "Location,Number of,Number of,Services arriving within " << AnsiString(ArrRange) << MinuteString << " with their arrival times and approach codes\n";
17211  TTFile3 << ",Platforms,Trains\n\n";
17212 
17213  Ptr1 = LocServiceTimesVector.begin();
17214  Ptr2 = Ptr1 + 1;
17215  while(Ptr2 != LocServiceTimesVector.end())
17216  {
17217  PreviousService = "";
17218  NumTrainsAtLoc = 0;
17219  ServiceAndRepeatNumTotal = "";
17220  NumPlats = 0;
17221  NumPlatsAtThisLocCalculated = false;
17222  BasicTime = "";
17223  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17224  {
17225  PreviousService = "";
17226  NumTrainsAtLoc = 0;
17227  ServiceAndRepeatNumTotal = "";
17228  NumPlats = 0;
17229  NumPlatsAtThisLocCalculated = false;
17230  BasicTime = "";
17231  Ptr1++;
17232  Ptr2++;
17233  if(Ptr2 == LocServiceTimesVector.end())
17234  {
17235  break;
17236  }
17237  }
17238  if(Ptr2 == LocServiceTimesVector.end())
17239  {
17240  break;
17241  }
17242  while(Ptr2->Location == Ptr1->Location)
17243  {
17244  PreviousService = "";
17245  NumTrainsAtLoc = 0;
17246  ServiceAndRepeatNumTotal = "";
17247  BasicTime = Ptr1->ArrTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17248  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17249  {
17250  break;
17251  }
17252  while(!WithinTimeRange(0, BasicTime, Ptr2->ArrTime, ArrRange) || ((Ptr1->ArrTime == "") && (Ptr2->ArrTime == "")))
17253  {
17254  BasicTime = Ptr2->ArrTime; //used to compare later times or last can exceed first
17255  Ptr1++;
17256  Ptr2++;
17257  if(Ptr2 == LocServiceTimesVector.end())
17258  {
17259  break;
17260  }
17261  if(Ptr2->Location != Ptr1->Location)
17262  {
17263  break;
17264  }
17265  }
17266  if(Ptr2 == LocServiceTimesVector.end())
17267  {
17268  break;
17269  }
17270  if(Ptr2->Location != Ptr1->Location)
17271  {
17272  break;
17273  }
17274  while(WithinTimeRange(1, BasicTime, Ptr2->ArrTime, ArrRange))
17275  {
17276  if((Ptr1->ArrTime == "") && (Ptr2->ArrTime == ""))
17277  {
17278  break;
17279  }
17280  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17281  {
17282  NumPlats = Track->NumberOfPlatforms(0, Ptr1->Location);
17283  NumPlatsAtThisLocCalculated = true;
17284  }
17285  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17286  {
17287  if(ServiceAndRepeatNumTotal == "")
17288  {
17289  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17290  NumTrainsAtLoc = 1;
17291  }
17292  else
17293  {
17294  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->ArrTime;
17295  }
17296  }
17297  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17298  if(ServiceAndRepeatNumTotal == "")
17299  {
17300  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17301  NumTrainsAtLoc = 1;
17302  }
17303  else
17304  {
17305  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->ArrTime;
17306  }
17307  Ptr1 = Ptr2;
17308  Ptr2++;
17309  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(2, BasicTime, Ptr2->ArrTime, ArrRange)))
17310  {
17311  int MaxNumberOfSameDirections = 0;
17312  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, true, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17313  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17314  {
17315 // ShowMessage("Error in arrival analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17316  TTFile3.close();
17317  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17318 // Utilities->CallLogPop(2224);
17319 // return false;
17320  }
17321  AnsiString Asterisk = "";
17322  if(MaxNumberOfSameDirections >= NumPlats)
17323  {
17324  Asterisk = "* ";
17325  }
17326  //print out a single line for number of trains at loc with all service refs
17327  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17328  ArrivalsPrinted = true;
17329  ServiceAndRepeatNumTotal = "";
17330  }
17331  if(Ptr2 == LocServiceTimesVector.end())
17332  {
17333  break;
17334  }
17335  if(Ptr2->Location != Ptr1->Location)
17336  {
17337  break;
17338  }
17339  }
17340  if(Ptr2 == LocServiceTimesVector.end())
17341  {
17342  break;
17343  }
17344  }
17345  }
17346  if(!ArrivalsPrinted)
17347  {
17348  TTFile3 << "Nothing to report for arrivals";
17349  }
17350  TTFile3 << "\n\n";
17351  }
17352  //end of routine for arrivals
17353  SequenceLog += "7\n";
17354  //departures
17355  if(DepChecked)
17356  {
17357  //sort in DepTime order for each location
17358  Ptr1 = LocServiceTimesVector.begin();
17359  Ptr2 = Ptr1 + 1;
17360  while(Ptr2 != LocServiceTimesVector.end())
17361  {
17362  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17363  {
17364  Ptr2++;
17365  if(Ptr2 == LocServiceTimesVector.end())
17366  {
17367  break;
17368  }
17369  }
17370  std::sort(Ptr1, Ptr2, &LocServiceTimesDepTimeSort);
17371  Ptr1 = Ptr2; //first entry with next name
17372  if(Ptr2 != LocServiceTimesVector.end())
17373  {
17374  Ptr2++;
17375  }
17376  }
17377 
17378  //routine for departures - number of trains departing within the specified range with services listed at the end
17379  TTFile3 << "Departure & pass analysis: an asterisk means that the number of same exit code departures and passes is equal to or greater than the number of platforms.\n";
17380  TTFile3 << "If the total number of departures and passes at the same time exceeds the number of platforms the 'Trains present at location analysis' will show an asterisk.\n\n";
17381  MinuteString = " minutes";
17382  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17383  if(DepRange == 1)
17384  {
17385  MinuteString = " minute";
17386  }
17387  TTFile3 << "Location,Number of,Number of,Services departing within " << AnsiString(DepRange) << MinuteString << " with their departure times and exit codes\n";
17388  TTFile3 << ",Platforms,Trains\n\n";
17389 
17390  Ptr1 = LocServiceTimesVector.begin();
17391  Ptr2 = Ptr1 + 1;
17392  while(Ptr2 != LocServiceTimesVector.end())
17393  {
17394  PreviousService = "";
17395  NumTrainsAtLoc = 0;
17396  ServiceAndRepeatNumTotal = "";
17397  NumPlats = 0;
17398  NumPlatsAtThisLocCalculated = false;
17399  BasicTime = "";
17400  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17401  {
17402  PreviousService = "";
17403  NumTrainsAtLoc = 0;
17404  ServiceAndRepeatNumTotal = "";
17405  NumPlats = 0;
17406  NumPlatsAtThisLocCalculated = false;
17407  BasicTime = "";
17408  Ptr1++;
17409  Ptr2++;
17410  if(Ptr2 == LocServiceTimesVector.end())
17411  {
17412  break;
17413  }
17414  }
17415  if(Ptr2 == LocServiceTimesVector.end())
17416  {
17417  break;
17418  }
17419  while(Ptr2->Location == Ptr1->Location)
17420  {
17421  PreviousService = "";
17422  NumTrainsAtLoc = 0;
17423  ServiceAndRepeatNumTotal = "";
17424  BasicTime = Ptr1->DepTime; //used to compare later times - later pointer contents have same or later times as sorted in time order
17425  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17426  {
17427  break;
17428  }
17429  while(!WithinTimeRange(3, BasicTime, Ptr2->DepTime, DepRange) || ((Ptr1->DepTime == "") && (Ptr2->DepTime == "")))
17430  {
17431  BasicTime = Ptr2->DepTime; //used to compare later times or last can exceed first
17432  Ptr1++;
17433  Ptr2++;
17434  if(Ptr2 == LocServiceTimesVector.end())
17435  {
17436  break;
17437  }
17438  if(Ptr2->Location != Ptr1->Location)
17439  {
17440  break;
17441  }
17442  }
17443  if(Ptr2 == LocServiceTimesVector.end())
17444  {
17445  break;
17446  }
17447  if(Ptr2->Location != Ptr1->Location)
17448  {
17449  break;
17450  }
17451  while(WithinTimeRange(4, BasicTime, Ptr2->DepTime, DepRange))
17452  {
17453  if((Ptr1->DepTime == "") && (Ptr2->DepTime == ""))
17454  {
17455  break;
17456  }
17457  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17458  {
17459  NumPlats = Track->NumberOfPlatforms(1, Ptr1->Location);
17460  NumPlatsAtThisLocCalculated = true;
17461  }
17462  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17463  {
17464  if(ServiceAndRepeatNumTotal == "")
17465  {
17466  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17467  NumTrainsAtLoc = 1;
17468  }
17469  else
17470  {
17471  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum + "," + Ptr1->DepTime;
17472  }
17473  }
17474  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ
17475  if(ServiceAndRepeatNumTotal == "")
17476  {
17477  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17478  NumTrainsAtLoc = 1;
17479  }
17480  else
17481  {
17482  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum + "," + Ptr2->DepTime;
17483  }
17484  Ptr1 = Ptr2;
17485  Ptr2++;
17486  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (!WithinTimeRange(5, BasicTime, Ptr2->DepTime, DepRange)))
17487  {
17488  int MaxNumberOfSameDirections = 0;
17489  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTArrDep(3, ServiceAndRepeatNumTotal, NumTrainsAtLoc, Ptr1->Location, false, AnalysisError, MaxNumberOfSameDirections); //sort into alphabetical order and remove duplicates
17490  if(AnalysisError) //has to be Ptr1->Location as Ptr2 loc may have changed
17491  {
17492 // ShowMessage("Error in departure analysis - file will be incomplete and/or corrupt. Please send railway and timetable files to railwayfeedback@gmail.com for investigation - thanks. Details: " + ServiceAndRepeatNumTotalOutput);
17493  TTFile3.close();
17494  throw Exception(ServiceAndRepeatNumTotalOutput.c_str());
17495 // Utilities->CallLogPop(2225);
17496 // return false;
17497  }
17498  AnsiString Asterisk = "";
17499  if(MaxNumberOfSameDirections >= NumPlats)
17500  {
17501  Asterisk = "* ";
17502  }
17503  //print out a single line for number of trains at loc with all service refs
17504  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << ServiceAndRepeatNumTotalOutput << '\n'; //no description as >1 service
17505  DeparturesPrinted = true;
17506  ServiceAndRepeatNumTotal = "";
17507  }
17508  if(Ptr2 == LocServiceTimesVector.end())
17509  {
17510  break;
17511  }
17512  if(Ptr2->Location != Ptr1->Location)
17513  {
17514  break;
17515  }
17516  }
17517  if(Ptr2 == LocServiceTimesVector.end())
17518  {
17519  break;
17520  }
17521  }
17522  }
17523  if(!DeparturesPrinted)
17524  {
17525  TTFile3 << "Nothing to report for departures";
17526  }
17527  TTFile3 << "\n\n";
17528  }
17529  //end of routine for departures
17530  SequenceLog += "8\n";
17531 
17532  //list trains at locations at same time
17533 
17534  if(AtLocChecked)
17535  {
17536  //sort in AtLocTime order for each location
17537  Ptr1 = LocServiceTimesVector.begin();
17538  Ptr2 = Ptr1 + 1;
17539  while(Ptr2 != LocServiceTimesVector.end())
17540  {
17541  while(Ptr2->Location == Ptr1->Location) //ends with Ptr2 one past same Location value as Ptr1
17542  {
17543  Ptr2++;
17544  if(Ptr2 == LocServiceTimesVector.end())
17545  {
17546  break;
17547  }
17548  }
17549  std::sort(Ptr1, Ptr2, &LocServiceTimesAtLocTimeSort);
17550  Ptr1 = Ptr2; //first entry with next name
17551  if(Ptr2 != LocServiceTimesVector.end())
17552  {
17553  Ptr2++;
17554  }
17555  }
17556 
17557  //print out simultaneous AtLocs (don't need range of times for AtLocs)
17558  TTFile3 << "Trains present at location analysis: an asterisk means that the number of trains at the location is greater than the number of platforms.\n\n";
17559  TTFile3 << "Location,Number of,Number of,Time,Services at the location at that time\n";
17560  TTFile3 << ",Platforms,Trains,\n\n";
17561  AnsiString ServiceAndRepeatNumTotal = "", ServiceAndRepeatNumTotalOutput = "";
17562  Ptr1 = LocServiceTimesVector.begin();
17563  Ptr2 = Ptr1 + 1;
17564  while(Ptr2 != LocServiceTimesVector.end())
17565  {
17566  PreviousService = "";
17567  ServiceAndRepeatNumTotal = "";
17568  NumTrainsAtLoc = 0;
17569  NumPlats = 0;
17570  NumPlatsAtThisLocCalculated = false;
17571  FrhCount = 0;
17572  while((Ptr2->Location != Ptr1->Location) || ((Ptr1->Location == "") && (Ptr2->Location == "")))
17573  {
17574  PreviousService = "";
17575  ServiceAndRepeatNumTotal = "";
17576  NumTrainsAtLoc = 0;
17577  NumPlats = 0;
17578  NumPlatsAtThisLocCalculated = false;
17579  FrhCount = 0;
17580  Ptr1++;
17581  Ptr2++;
17582  if(Ptr2 == LocServiceTimesVector.end())
17583  {
17584  break;
17585  }
17586  }
17587  if(Ptr2 == LocServiceTimesVector.end())
17588  {
17589  break;
17590  }
17591  while(Ptr2->Location == Ptr1->Location)
17592  {
17593  if(Ptr1->FrhMarker == "Frh") //this test is made here and each time Ptr1 increases with Ptr1 & 2 at same loc so as to catch them all
17594  {
17595  FrhCount++;
17596  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17597  }
17598  PreviousService = "";
17599  NumTrainsAtLoc = 0;
17600  ServiceAndRepeatNumTotal = "";
17601  if((Ptr1->Location == "") && (Ptr2->Location == ""))
17602  {
17603  break;
17604  }
17605  while((Ptr2->AtLocTime != Ptr1->AtLocTime) || ((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == "")))
17606  {
17607  Ptr1++;
17608  if(Ptr1->FrhMarker == "Frh")
17609  {
17610  FrhCount++;
17611  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17612  }
17613  Ptr2++;
17614  if(Ptr2 == LocServiceTimesVector.end())
17615  {
17616  break;
17617  }
17618  if(Ptr2->Location != Ptr1->Location)
17619  {
17620  break;
17621  }
17622  }
17623  if(Ptr2 == LocServiceTimesVector.end())
17624  {
17625  break;
17626  }
17627  if(Ptr2->Location != Ptr1->Location)
17628  {
17629  break;
17630  }
17631  while(Ptr2->AtLocTime == Ptr1->AtLocTime)
17632  {
17633  if((Ptr1->AtLocTime == "") && (Ptr2->AtLocTime == ""))
17634  {
17635  break;
17636  }
17637  if(!NumPlatsAtThisLocCalculated) //num plats at relevant location, reset when locations change
17638  {
17639  NumPlats = Track->NumberOfPlatforms(2, Ptr1->Location);
17640  NumPlatsAtThisLocCalculated = true;
17641  }
17642  if(Ptr1->ServiceAndRepeatNum != PreviousService) //don't print it twice if same as last - as will be if >1 service at same loc at same time
17643  {
17644  if(ServiceAndRepeatNumTotal == "")
17645  {
17646  ServiceAndRepeatNumTotal = Ptr1->ServiceAndRepeatNum;
17647  NumTrainsAtLoc = 1;
17648  }
17649  else
17650  {
17651  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr1->ServiceAndRepeatNum;
17652  }
17653  }
17654  PreviousService = Ptr2->ServiceAndRepeatNum; //last service at relevant time, reset when times differ, has to be Ptr2 to compare Ptr1 at next round when incremented
17655  if(ServiceAndRepeatNumTotal == "")
17656  {
17657  ServiceAndRepeatNumTotal = Ptr2->ServiceAndRepeatNum;
17658  NumTrainsAtLoc = 1;
17659  }
17660  else
17661  {
17662  ServiceAndRepeatNumTotal = ServiceAndRepeatNumTotal + "," + Ptr2->ServiceAndRepeatNum;
17663  }
17664  Ptr1 = Ptr2;
17665  if(Ptr1->FrhMarker == "Frh")
17666  {
17667  FrhCount++;
17668  Ptr1->FrhMarker = "FrhCounted"; //to avoid double counting
17669  }
17670  Ptr2++;
17671  if((Ptr2 == LocServiceTimesVector.end()) || (Ptr2->Location != Ptr1->Location) || (Ptr2->AtLocTime != Ptr1->AtLocTime))
17672  {
17673 //old text //only print out if no remainers (1st condition), change in remainers (2nd condition) or change in ServiceAndRepeatNumTotalOutput, and >1 train (later condition)
17674 //new text //don't print out if all remainers or if only 1 train at loc
17675  ServiceAndRepeatNumTotalOutput = ConsolidateSARNTAtLoc(1, ServiceAndRepeatNumTotal, NumTrainsAtLoc); //sort into alphabetical order, remove duplicates, and calculate new value for NumTrainsAtLoc
17676 //old condits if((FrhCount == 0) || (FrhCount != LastFrhCount) || (PreviousServiceAndRepeatNumTotalOutput != ServiceAndRepeatNumTotalOutput))//don't print if same output
17677 /*new condits*/ if((NumTrainsAtLoc > 1) && ((FrhCount < NumTrainsAtLoc) || (FrhCount != LastFrhCount)))
17678  {
17679  AnsiString Asterisk = "";
17680  if(NumTrainsAtLoc > NumPlats)
17681  {
17682  Asterisk = "* ";
17683  }
17684  //print out a single line for number of trains at loc with all service refs
17685  if(FrhCount == 0)
17686  {
17687  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << "," << ServiceAndRepeatNumTotalOutput << '\n';
17688  }
17689  else if(FrhCount == 1)
17690  {
17691  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (1 remains here)," << ServiceAndRepeatNumTotalOutput << '\n';
17692  }
17693  else
17694  {
17695  TTFile3 << Asterisk << Ptr1->Location << "," << NumPlats << "," << NumTrainsAtLoc << "," << Ptr1->AtLocTime << " (" << FrhCount << " remain here)," << ServiceAndRepeatNumTotalOutput << '\n';
17696  }
17697  LastFrhCount = FrhCount;
17698  PreviousServiceAndRepeatNumTotalOutput = ServiceAndRepeatNumTotalOutput;
17699  AtLocsPrinted = true;
17700  ServiceAndRepeatNumTotal = "";
17701  }
17702  }
17703  if(Ptr2 == LocServiceTimesVector.end())
17704  {
17705  break;
17706  }
17707  if(Ptr2->Location != Ptr1->Location)
17708  {
17709  break;
17710  }
17711  }
17712  if(Ptr2 == LocServiceTimesVector.end())
17713  {
17714  break;
17715  }
17716  }
17717  }
17718  if(!AtLocsPrinted)
17719  {
17720  TTFile3 << "Nothing to report for trains at locations";
17721  }
17722  TTFile3 << "\n\n";
17723  //end of simultaneous AtLocs
17724  }
17725  SequenceLog += "9\n";
17726 /*
17727  //print out the full vector here for testing purposes
17728  TTFile3 << "Full LocServiceTimesVector\n\n";
17729  TTFile3 << "Location,AtLocTime,ArrTime,DepTime,ServiceAndRepeatNum,Description\n\n";
17730 
17731  for(TLocServiceTimesVector::iterator Ptr = LocServiceTimesVector.begin(); Ptr != LocServiceTimesVector.end(); Ptr++)
17732  {
17733  TTFile3 << Ptr->Location << "," << Ptr->AtLocTime << "," << Ptr->ArrTime << "," << Ptr->DepTime << "," << Ptr->ServiceAndRepeatNum << "," << Ptr->FrhMarker << '\n';
17734  }
17735 
17736  TTFile3 << "\n\n\n";
17737 */
17738 /*cdt analysis - added at v2.10.0
17739 2 pass system: 1st extract as a single service all Snt (or Snt-sh) starts, with Fns/Sns links combined (and F-nshs/Sns-sh) (though add a new
17740 changeover code [chr XXXX - 'change ref + new reference] until come to Fjo, Frh, Frh-sh, Fer (ignore exit loc as can't stop there), ignore jbos &
17741 repeats, but with fsp & rsp store all the foregoing service entries along with the split reference & add that to the relevant Sfs entry as a new
17742 service. For shuttles with feeder start with feeder & progress into shuttle, ending when finish & remain here or progressing into the finishing
17743 service.
17744 
17745 Use The TrainDataVectorCopy as that has all unique service refs.
17746 
17747 2nd run the cdt checker similar to that in SecondPassActions, but where a same name found either side of a changeover code quote both refs. Add a
17748 similar unexpected cdt check where if have different locs either side of a cdt then may be inappropriate.
17749 
17750 First create a new TrainDataVector from earlier copy as above with single services
17751 */
17752  if(DirChecked)
17753  {
17754  //direction analysis added at v2.10.0
17755  TTrainDataEntry SingleServiceEntry, PartServiceEntry, NewPartServiceEntry, TempEntry;
17756  TTrainDataVector SingleServiceVector, PartServiceVector;
17757 
17758  //find new train services (Snt or Snt-sh) & remember that entries can be in any order
17759  //NB: ALWAYS use OtherHeadCode (which is now a service reference) for any follow-on service
17760  TTFile3 << "Train direction analysis - consisting of train facing directions on creation and possible missing or questionable changes of direction:\n\n";
17761  for(unsigned int x = 0; x < TrainDataVectorCopy.size(); x++)
17762  {
17763  TTrainDataEntry TDE = TrainDataVectorCopy.at(x);
17764  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17765  {
17766  TDE.ActionVector.erase(&TDE.ActionVector.back()); //strip repeat entry if present
17767  }
17768  const TActionVector &AV = TDE.ActionVector;
17769  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17770  {
17771  SingleServiceEntry = TDE;
17772  TActionVector &SSAV = SingleServiceEntry.ActionVector;
17773  for(unsigned int y = 0; y < SSAV.size(); y++)
17774  {
17775  if((SSAV.at(y).Command == "Fjo") || (SSAV.at(y).Command == "Frh") || (SSAV.at(y).Command == "Fer") || (SSAV.at(y).Command == "Frh-sh"))
17776  {
17777  SingleServiceVector.push_back(SingleServiceEntry); //push the complete entry
17778  break; //finished with this one
17779  }
17780  else if((SSAV.at(y).Command == "fsp") || (SSAV.at(y).Command == "rsp"))
17781  {
17782  PartServiceEntry = TDE; //start with complete entry
17783  PartServiceEntry.ActionVector.clear(); //clear AV
17784  for(unsigned int z = 0; z <= y; z++)
17785  {
17786  PartServiceEntry.ActionVector.push_back(TDE.ActionVector.at(z)); //add back all AVs up to & inc fsp/rsp
17787  if(z == y)
17788  {
17789  PartServiceEntry.ActionVector.at(z).Command = "chr-sp"; //change split command to chr
17790  PartServiceEntry.ActionVector.at(z).OtherHeadCode = PartServiceEntry.ActionVector.at(z).LinkedTrainEntryPtr->ServiceReference;
17791  }
17792  }
17793  PartServiceVector.push_back(PartServiceEntry);
17794  if(SSAV.at(y).Command == "fsp")
17795  {
17796  SSAV.at(y).Command = "Front split - original service continues below";
17797  SSAV.at(y).OtherHeadCode = "";
17798  }
17799  if(SSAV.at(y).Command == "rsp")
17800  {
17801  SSAV.at(y).Command = "Rear split - original service continues below";
17802  SSAV.at(y).OtherHeadCode = "";
17803  }
17804  //don't break & continue here because the original train carries on
17805  }
17806  else if(SSAV.at(y).Command == "Fns")
17807  {
17808  SSAV.at(y).Command = "chr-Fns";
17809  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17810  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17811  break; //from y loop
17812  }
17813  else if(SSAV.at(y).Command == "Fns-sh")
17814  {
17815  SSAV.at(y).Command = "chr-Fns-sh";
17816  SSAV.at(y).OtherHeadCode = SSAV.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17817  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17818  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17819  break; //from y loop
17820  }
17821  else if(SSAV.at(y).Command == "F-nshs")
17822  {
17823  SSAV.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17824  SSAV.at(y).OtherHeadCode = SSAV.at(y).LinkedTrainEntryPtr->ServiceReference;
17825  SSAV.at(y).NonRepeatingShuttleLinkHeadCode = "";
17826  PartServiceVector.push_back(SingleServiceEntry); //not complete yet
17827  break; //from y loop
17828  }
17829  }
17830  }
17831  }
17832  SequenceLog += "10\n";
17833  //now have all complete entries in SingleServiceVector and all part services in PartServiceVector but without any follow-ons
17834 
17835  //Now add Sns & Sns-sh services to PartServiceVector entries
17836  AnsiString NextRef;
17837  while(!PartServiceVector.empty())
17838  {
17839  PartServiceEntry = PartServiceVector.at(0); //deal with front entry and add new entries at the back
17840  for(unsigned int y = 0; y < PartServiceEntry.ActionVector.size(); y++)
17841  {
17842  if(PartServiceEntry.ActionVector.at(y).Command.SubString(1,3) == "chr")
17843  {
17844  NextRef = PartServiceEntry.ActionVector.at(y).OtherHeadCode;
17845  }
17846  }
17847  //find it in TrainDataVectorCopy
17848  bool FinishType = true, FoundFlag = false;
17849  while(FinishType)
17850  {
17851  TempEntry = GetServiceFromVector(0, NextRef, TrainDataVectorCopy, FinishType, FoundFlag); //FinishType is a bool where false = Final (Fjo, Frh, Fer, or
17852  //Frh-sh); true = MoreToCome (Fns, Fns-sh, F-nshs)
17853  if(FoundFlag)
17854  {
17855  for(unsigned int y = 1; y < TempEntry.ActionVector.size(); y++) //starts at 1 as that is the entry after the start entry
17856  {
17857  if((TempEntry.ActionVector.at(y).Command == "") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17858  {
17859  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17860  }
17861  else if((TempEntry.ActionVector.at(y).Command[1] != 'F') && (TempEntry.ActionVector.at(y).Command != "fsp") && (TempEntry.ActionVector.at(y).Command != "rsp") && (TempEntry.ActionVector.at(y).FormatType != Repeat))
17862  {
17863  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17864  }
17865  else
17866  {
17867  if((TempEntry.ActionVector.at(y).Command == "Fjo") || (TempEntry.ActionVector.at(y).Command == "Frh") || (TempEntry.ActionVector.at(y).Command == "Fer") || (TempEntry.ActionVector.at(y).Command == "Frh-sh"))
17868  {
17869  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17870  SingleServiceVector.push_back(PartServiceVector.at(0)); //push the complete entry
17871  PartServiceVector.erase(&PartServiceVector.at(0));
17872  break; //from y loop
17873  }
17874  else if((TempEntry.ActionVector.at(y).Command == "fsp") || (TempEntry.ActionVector.at(y).Command == "rsp"))
17875  {
17876  NewPartServiceEntry = PartServiceVector.at(0); //covers everything up to but excluding the split
17877  NewPartServiceEntry.ActionVector.push_back(TempEntry.ActionVector.at(y)); //now includes the split
17878  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).Command = "chr-sp"; //change split command to chr
17879  NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).OtherHeadCode = NewPartServiceEntry.ActionVector.at(NewPartServiceEntry.ActionVector.size() - 1).LinkedTrainEntryPtr->ServiceReference;
17880  PartServiceVector.push_back(NewPartServiceEntry); //new entry for the split service
17881  if(TempEntry.ActionVector.at(y).Command == "fsp")
17882  {
17883  TempEntry.ActionVector.at(y).Command = "Front split - original service continues below";
17884  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17885  }
17886  if(TempEntry.ActionVector.at(y).Command == "rsp")
17887  {
17888  TempEntry.ActionVector.at(y).Command = "Rear split - original service continues below";
17889  TempEntry.ActionVector.at(y).OtherHeadCode = "";
17890  }
17891  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y));
17892  }
17893  else if(TempEntry.ActionVector.at(y).Command == "Fns")
17894  {
17895  TempEntry.ActionVector.at(y).Command = "chr-Fns";
17896  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17897  TempEntry.ActionVector.at(y).OtherHeadCode = NextRef;
17898  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17899  break; //from y loop
17900  }
17901  else if(TempEntry.ActionVector.at(y).Command == "Fns-sh")
17902  {
17903  TempEntry.ActionVector.at(y).Command = "chr-Fns-sh";
17904  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17905  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17906  NextRef = TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkEntryPtr->ServiceReference;
17907  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17908  break; //from y loop
17909  }
17910  else if(TempEntry.ActionVector.at(y).Command == "F-nshs")
17911  {
17912  TempEntry.ActionVector.at(y).Command = "chr-F-nshs"; //NonRepeatingShuttleLinkHeadCode is the shuttle headcode
17913  TempEntry.ActionVector.at(y).OtherHeadCode = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17914  TempEntry.ActionVector.at(y).NonRepeatingShuttleLinkHeadCode = "";
17915  NextRef = TempEntry.ActionVector.at(y).LinkedTrainEntryPtr->ServiceReference;
17916  PartServiceVector.at(0).ActionVector.push_back(TempEntry.ActionVector.at(y)); //not complete yet
17917  break; //from y loop
17918  }
17919  }
17920  }
17921  }
17922  else
17923  {
17924  SequenceLog += + "11\n";
17925  throw Exception("Unable to find service reference " + NextRef + " Last ref checked = " + TempEntry.ServiceReference);
17926  }
17927  }
17928  }
17929  if(!PartServiceVector.empty())
17930  {
17931  SequenceLog += "12\n";
17932  throw Exception("PartServiceVector should be empty here - size = " + PartServiceVector.size());
17933  }
17934  SequenceLog += "13\n";
17935  /*
17936  form:-
17937  HeadCode[;Description (plain text, no commas or semicolons)][;StartSpeed(kph); MaxRunningSpeed(kph); Mass(tonnes, prog converts to kg);
17938  MaxBrakeRate(tonnes force, prog converts to m/s/s); & gross power(kW, prog converts to power at rail in w)
17939  then multiple entries, separated by commas, of the form:-
17940 
17941  HH:MM;Snt;RearStartIdent FrontStartIdent }StartNew }
17942  HH:MM;Snt-sh;RearStartIdent FrontStartIdent;Fsh HeadCode }SNTShuttle }
17943  HH:MM;Sns-sh;Fxx-sh HeadCode;F-nshs HeadCode (non-repeating)}SNSShuttle }
17944 
17945  HH:MM;Command;HeadCode (Sfs Sns jbo fsp rsp Fns Fjo Frh-sh) }TimeCmdHeadCode } Train action entries
17946  HH:MM;F-nshs;NonRepeatingShuttleLinkHeadCode }FNSNonRepeatToShuttle }
17947  HH:MM;Sns-fsh;NonRepeatingShuttleLinkHeadCode }SNSNonRepeatFromShuttle }
17948 
17949  HH:MM;Command (cdt) }TimeCmd }
17950  HH:MM;Location (arr & dep) }TimeLoc }
17951  HH:MM;HH:MM;Location }TimeTimeLoc }
17952  HH:MM;pas;Location }PassTime }
17953  HH:MM;Fns-sh;Snx-sh HeadCode;Sns-fsh HeadCode (non-rep) }FSHNewService }
17954  HH:MM;Fer;set of allowable IDs }ExitRailway }
17955  Command (Frh only) }FinRemHere }
17956 
17957  R;mm;dd;nn. Repeat Repeat entry
17958 
17959  Formats:
17960 
17961  Command only: Frh
17962  Time;Command: cdt
17963  Time;Command;Headcode: Sfs Sns jbo fsp rsp Fns Fjo Frh-sh F-nshs Sns-fsh
17964  Time;Command;2 Element IDs: Snt
17965  Time;Comand;n Element IDs: Fer
17966  Time;Command;rep Headcode;nonrep Headcode: Sns-sh Fns-sh
17967  Time;Command;2 Element IDs;Headcode Snt-sh
17968  Time;Command;Location pas
17969  Time;Location Arr Dep
17970  Time;Time;Location Arr & dep together
17971  */
17972 
17973 /*
17974 Perform the starting direction check (Snt & Snt-sh entries). Starts with the train's front element,
17975 checking forwards until it comes to a continuation (no report), a location name that is not null and
17976 different to the train's front element name (whether null or not) (no report), a leading point
17977 (no report) or buffers (report).
17978 */
17979  bool BufferFacingUnReportedFlag = true;
17980  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
17981  {
17982  TTrackElement ThisElement, NextElement;
17983  TTrainDataEntry TDE = SingleServiceVector.at(x);
17984  if(TDE.ActionVector.at(TDE.ActionVector.size() - 1).FormatType == Repeat)
17985  {
17986  SequenceLog += "13a\n";
17987  throw Exception("Repeat entry present in SingleServiceVector at position " + x);
17988  }
17989  const TActionVector &AV = TDE.ActionVector;
17990  if((AV.at(0).Command == "Snt") || (AV.at(0).Command == "Snt-sh"))
17991  {
17992  bool BufferFlag = false;
17993  int FrontTVPos = AV.at(0).FrontStartOrRepeatDigits;
17994  int RearTVPos = AV.at(0).RearStartOrRepeatMins;
17995  AnsiString FrontLocName = AV.at(0).LocationName;
17996  int NextEntryPos, NextExitPos;
17997  ThisElement = Track->TrackElementAt(1395, FrontTVPos);
17998  int ThisExitPos;
17999  if(ThisElement.Conn[0] == RearTVPos)
18000  {
18001  ThisExitPos = 1;
18002  }
18003  else if(ThisElement.Conn[1] == RearTVPos)
18004  {
18005  ThisExitPos = 0;
18006  }
18007  else if(ThisElement.Conn[2] == RearTVPos)
18008  {
18009  ThisExitPos = 3;
18010  }
18011  else if(ThisElement.Conn[3] == RearTVPos)
18012  {
18013  ThisExitPos = 2;
18014  }
18015  if((ThisElement.TrackType == Buffers) && (ThisExitPos == 0))//pos 0 is the buffer
18016  {
18017  BufferFlag = true;
18018  }
18019  else //continue tracking forwards
18020  {
18021  while(true)
18022  {
18023  if(ThisElement.Conn[ThisExitPos] == -1)
18024  {
18025  SequenceLog = "13b\n";
18026  throw Exception("ThisElement connects to -1 for " + TDE.ServiceReference);
18027  }
18028  NextElement = Track->TrackElementAt(1396, ThisElement.Conn[ThisExitPos]);
18029  NextEntryPos = ThisElement.ConnLinkPos[ThisExitPos];
18030  if((NextElement.TrackType == Points) && ((NextEntryPos == 0) || (NextEntryPos == 2))) //leading points
18031  {
18032  BufferFlag = false; //should already be false
18033  break;
18034  }
18035  else if(NextElement.TrackType == Continuation)
18036  {
18037  BufferFlag = false;
18038  break;
18039  }
18040  else if((NextElement.ActiveTrackElementName != "") && (NextElement.ActiveTrackElementName != FrontLocName))
18041  {
18042  BufferFlag = false;
18043  break;
18044  }
18045  else if(NextElement.TrackType == Buffers)
18046  {
18047  BufferFlag = true;
18048  break;
18049  }
18050  else if((NextElement.TrackType == Points) && ((NextEntryPos == 1) || (NextEntryPos == 3))) //trailing points
18051  {
18052  ThisElement = NextElement;
18053  ThisExitPos = 0;
18054  continue;
18055  }
18056  else
18057  {
18058  if(NextEntryPos == 0)
18059  {
18060  NextExitPos = 1;
18061  }
18062  else if(NextEntryPos == 1)
18063  {
18064  NextExitPos = 0;
18065  }
18066  else if(NextEntryPos == 2)
18067  {
18068  NextExitPos = 3;
18069  }
18070  else if(NextEntryPos == 3)
18071  {
18072  NextExitPos = 2;
18073  }
18074  }
18075  ThisElement = NextElement;
18076  ThisExitPos = NextExitPos;
18077  }
18078  }
18079  if(BufferFlag)
18080  {
18081  if(BufferFacingUnReportedFlag)
18082  {
18083  TTFile3 << "Train facing direction on creation analysis:-\n\n";
18084  BufferFacingUnReportedFlag = false;
18085  }
18086  TTFile3 << "Service " + TDE.ServiceReference + " facing buffers on creation\n";
18087  }
18088  }
18089  }
18090  if(BufferFacingUnReportedFlag)
18091  {
18092  TTFile3 << "Nothing to report for train facing directions\n\n";
18093  }
18094  else
18095  {
18096  TTFile3 << '\n';
18097  }
18098  SequenceLog += "13c\n";
18099 
18100  //Perform the missing cdt check. Check every entry simiar to the check in SecondPassActions and if find any print out the full sequence and service entries
18101  AnsiString LocationNameToBeChecked = "";
18102  bool MissingcdtUnreportedFlag = true;
18103  TNumList MarkerList;
18104  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18105  {
18106  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18107  unsigned int y = 0;
18108  int FirstInstance = 9999, SecondInstance = 9999; //9999 ensures wont be marked if not changed
18109  bool FullBreak = false;
18110  MarkerList.clear();
18111  const TActionVectorEntry &AVEntry0 = TDEntry.ActionVector.at(0);
18112  // first discard unlocated Snt entries as they don't have location name set
18113  if((AVEntry0.Command == "Snt") && (AVEntry0.LocationType == EnRoute))
18114  {
18115  y = 1;
18116  }
18117  while((y < TDEntry.ActionVector.size()) && !FullBreak)
18118  // need to check each location name separately in turn, skipped for SignallerControl entries
18119  {
18120  if((TDEntry.ActionVector.at(y).Command == "Fer") || (TDEntry.ActionVector.at(y).FormatType == Repeat) ||
18121  (TDEntry.ActionVector.at(y).Command == "Fjo") || (TDEntry.ActionVector.at(y).Command == "Frh") ||
18122  (TDEntry.ActionVector.at(y).Command == "Frh-sh"))
18123  {
18124  break; // out of the 'while' loop since have reached the end
18125  }
18126  LocationNameToBeChecked = TDEntry.ActionVector.at(y).LocationName;
18127  FirstInstance = y;
18128  for(unsigned int z = y; z < TDEntry.ActionVector.size(); z++)
18129  {
18130  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
18131  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) ||
18132  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18133  (AVEntry.Command == "Frh-sh"))
18134  {
18135  break; // out of the 'z' loop since have reached the end & 'Fer' & 'Repeat' have no location name set
18136  }
18137  if(AVEntry.Command == "cdt")
18138  {
18139  break; // out of the 'z' loop since the check is only valid up to a change of direction
18140  }
18141  if(AVEntry.LocationName == LocationNameToBeChecked)
18142  {
18143  continue; // keep going while name same
18144  }
18145  if(AVEntry.LocationName != LocationNameToBeChecked)
18146  // if name different check forwards to see if repeats
18147  {
18148  for(unsigned int a = z; a < TDEntry.ActionVector.size(); a++)
18149  {
18150  if(TDEntry.ActionVector.at(a).Command == "cdt")
18151  {
18152  break; // out of the 'a' & 'z' loops since the check is only valid up to a change of direction
18153  }
18154  if(TDEntry.ActionVector.at(a).LocationName == LocationNameToBeChecked)
18155  {
18156  SecondInstance = a;
18157  AnsiString Sequence = TDEntry.ServiceReference;
18158  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18159  {
18160  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18161  {
18162  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18163  }
18164  }
18165  if(MissingcdtUnreportedFlag)
18166  {
18167  TTFile3 << "Possibly missing changes of direction - these will be missing unless the service travels in a loop back to the locations marked:-\n\n";
18168  }
18169  TTFile3 << LocationNameToBeChecked << " is listed twice with no direction change between in service sequence: " << Sequence << "\n\n";
18170  MarkerList.push_back(FirstInstance);
18171  MarkerList.push_back(SecondInstance);
18172  SingleServiceOutput(0, x, MarkerList, SingleServiceVector, TTFile3);
18173  MissingcdtUnreportedFlag = false;
18174  FullBreak = true; //no more checks for this sequence
18175  break; //out of the a & z loops
18176  }
18177  }
18178  break; // out of the 'z' loop since have checked 'a' as far as need to
18179  }
18180  }
18181  y++;
18182  }
18183  }
18184  if(MissingcdtUnreportedFlag)
18185  {
18186  TTFile3 << "Nothing to report for missing changes of direction\n\n";
18187  }
18188  else
18189  {
18190  TTFile3 << '\n';
18191  }
18192  SequenceLog += "14\n";
18193 
18194 /* Perform the questionable cdt check. Examine each service in the SingleServiceVector, and if don't find the same
18195  name either side of a cdt (before the next cdt) then flag as a questionable cdt.
18196  Method: Have an outer loop for each service that looks for cdts. When found work backwards to the last cdt or beginning and std::list all the
18197  locations excluding the cdt location. Then work forwards to the next cdt or the end and do the same. Sort each list and make unique (duplicated
18198  names on one side of a cdt already checked either by the tt validator or the missing cdt check. Then compare the two lists and if any location
18199  included in both then ok, else report as questionable. If one list is empty then it is reported.
18200 */
18201  typedef std::list<AnsiString> TLocList;
18202  TLocList BackwardList, ForwardList;
18203  bool IntroLineNeeded = true;
18204  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18205  {
18206  unsigned int cdtPosition = 9999;
18207  AnsiString cdtLocation = "";
18208  bool FoundSameName = false;
18209  MarkerList.clear();
18210  const TTrainDataEntry &TDEntry = SingleServiceVector.at(x);
18211  for(unsigned int y = 0; y < TDEntry.ActionVector.size(); y++)
18212  // need to check each location name separately in turn, skipped for SignallerControl entries
18213  {
18214  BackwardList.clear();
18215  ForwardList.clear();
18216  const TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(y);
18217  if((AVEntry.Command == "Fer") || (AVEntry.FormatType == Repeat) || //shouldn't be any repeats
18218  (AVEntry.Command == "Fjo") || (AVEntry.Command == "Frh") ||
18219  (AVEntry.Command == "Frh-sh"))
18220  {
18221  if(MarkerList.empty())
18222  {
18223  break; // out of the 'y' loop since have reached the end & nothing to report
18224  }
18225  else
18226  {
18227  AnsiString Sequence = TDEntry.ServiceReference;
18228  for(unsigned int b = 0; b < TDEntry.ActionVector.size(); b++)
18229  {
18230  if(TDEntry.ActionVector.at(b).Command.SubString(1,3) == "chr")
18231  {
18232  Sequence = Sequence + AnsiString(" -> ") + TDEntry.ActionVector.at(b).OtherHeadCode;
18233  }
18234  }
18235  MarkerList.sort();
18236  if(IntroLineNeeded)
18237  {
18238  TTFile3 << "Questionable change of direction analysis.\n\n";
18239  TTFile3 << "For marked changes of direction there are no same-name locations listed both above and below.\n";
18240  TTFile3 << "These changes of direction are probably valid for movements to and from depots but all should be checked to\n";
18241  TTFile3 << "make sure that none has been included incorrectly:\n\n";
18242  IntroLineNeeded = false;
18243  }
18244  TTFile3 << "Service sequence " << Sequence << " contains questionable changes of direction:-\n\n";
18245  SingleServiceOutput(1, x, MarkerList, SingleServiceVector, TTFile3);
18246  break;
18247  }
18248  }
18249  if(AVEntry.Command != "cdt")
18250  {
18251  continue; //only looking for cdts
18252  }
18253  //here have found a cdt
18254  cdtPosition = y;
18255  cdtLocation = AVEntry.LocationName;
18256  for(int z = y - 1; z >= 0; z--)
18257  {
18258  const TActionVectorEntry &AVEntry2 = TDEntry.ActionVector.at(z);
18259  if(AVEntry2.Command == "cdt")
18260  {
18261  break; //don't look further back than the last cdt
18262  }
18263  if((AVEntry2.LocationName != "") && (AVEntry2.LocationName != cdtLocation)) //if an earlier entry == cdtLocation will have been picked up in an earlier check
18264  {
18265  BackwardList.push_back(AVEntry2.LocationName);
18266  }
18267  }
18268  BackwardList.sort();
18269  BackwardList.unique();
18270  for(unsigned int z = y + 1; z < TDEntry.ActionVector.size(); z++)
18271  {
18272  const TActionVectorEntry &AVEntry3 = TDEntry.ActionVector.at(z);
18273  if((AVEntry3.Command == "Fer") || (AVEntry3.FormatType == Repeat) ||
18274  (AVEntry3.Command == "Fjo") || (AVEntry3.Command == "Frh") ||
18275  (AVEntry3.Command == "Frh-sh") || (AVEntry3.Command == "cdt"))
18276  {
18277  break; // out of the 'z' loop since have reached another cdt or the end
18278  }
18279  if((AVEntry3.LocationName != "") && (AVEntry3.LocationName != cdtLocation)) //if a later entry == cdtLocation will have been picked up in an earlier check
18280  {
18281  ForwardList.push_back(AVEntry3.LocationName);
18282  }
18283  }
18284  ForwardList.sort();
18285  ForwardList.unique();
18286  FoundSameName = false;
18287  //now have both lists compiled (may be empty) so check for same name in both & report if don't find any
18288  if(!BackwardList.empty() && !ForwardList.empty())
18289  {
18290  for(TLocList::iterator BLIt = BackwardList.begin(); BLIt != BackwardList.end(); BLIt++)
18291  {
18292  for(TLocList::iterator FLIt = ForwardList.begin(); FLIt != ForwardList.end(); FLIt++)
18293  {
18294  if(*BLIt == *FLIt)
18295  {
18296  FoundSameName = true;
18297  }
18298  }
18299  }
18300  }
18301  if(!FoundSameName) //report the inability to find same name
18302  {
18303  MarkerList.push_back(cdtPosition);
18304  }
18305  }
18306  }
18307  if(IntroLineNeeded)
18308  {
18309  TTFile3 << "Nothing to report for questionable changes of direction\n\n";
18310  }
18311  else
18312  {
18313  TTFile3 << '\n';
18314  }
18315 /*
18316 //print all SSVector for diagnostic purposes
18317  TTFile3 << "Whole SSVector\n\n";
18318  TNumList EmptyList;
18319  for(unsigned int x = 0; x < SingleServiceVector.size(); x++)
18320  {
18321  SingleServiceOutput(, x, EmptyList, SingleServiceVector, TTFile3);
18322  }
18323 */
18324  }
18325  SequenceLog += "15\n";
18326  TTFile3.close();
18327  Utilities->CallLogPop(2212);
18328  return(true);
18329  }
18330 
18331  catch(const Exception &e) //non error catch
18332  {
18333  AnsiString TTErrorFileName = "Analysis Error.txt";
18334  TTErrorFileName = CurDir + "\\Formatted timetables\\" + TTErrorFileName;
18335  std::ofstream TTError(TTErrorFileName.c_str());
18336  if(TTError == 0)
18337  {
18338  ShowMessage("Analysis error file failed to open - can't be created");
18339  Utilities->CallLogPop(2233);
18340  return(false);
18341  }
18342  AnsiString TimeNow = TDateTime::CurrentDateTime().FormatString("dd-mm-yyyy hh.nn.ss");
18343  TTError << TimeNow.c_str() << '\n' << ArrRange << '\n' << ArrChecked << '\n' << DepRange << '\n' <<
18344  DepChecked << '\n' << AtLocChecked << '\n' << SequenceLog << '\n' << AnsiString(e.Message);
18345 
18346  TTError.close();
18347  ShowMessage("Error in Conflict Analysis: A file called 'Analysis Error.txt' has been created in your Formatted timetables folder. Please send this file together with your railway and timetable files to railwayfeedback@gmail.com for investigation - many thanks");
18348  Utilities->CallLogPop(2226);
18349  return(false);
18350  }
18351 }
18352 
18353 // ---------------------------------------------------------------------------
18354 void TTrainController::SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
18355 {
18356  Utilities->CallLog.push_back(Utilities->TimeStamp() + AnsiString(SSVectorNumber) + ',' + ",SingleServiceOutput");
18357  if((SSVectorNumber < 0) || ((unsigned int)SSVectorNumber >= SingleServiceVector.size()))
18358  {
18359  throw Exception("SSVectorNumber out of range, = " + AnsiString(SSVectorNumber) + ", Vector size = " + SingleServiceVector.size());
18360  }
18361  TTrainDataEntry SingleService = SingleServiceVector.at(SSVectorNumber);
18362  {
18363  VecFile << ",Initial service reference " << SingleService.ServiceReference + '\n';
18364  AnsiString Marker = "";
18365  for(unsigned int x = 0; x < SingleService.ActionVector.size(); x++)
18366  {
18367  Marker = ',';
18368  for(TNumListIterator MLIt = MarkerList.begin(); MLIt != MarkerList.end(); MLIt++)
18369  {
18370  if(int(x) == *MLIt)
18371  {
18372  Marker = "-->,";
18373  break;
18374  }
18375  }
18376  TActionVectorEntry AVE = SingleService.ActionVector.at(x);
18377  if(AVE.FormatType == StartNew)
18378  {
18379  AnsiString RearID = Track->TrackElementAt(1397, AVE.RearStartOrRepeatMins).ElementID;
18380  AnsiString FrontID = Track->TrackElementAt(1398, AVE.FrontStartOrRepeatDigits).ElementID;
18381  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << '\n';
18382  }
18383  if(AVE.FormatType == SNTShuttle)
18384  {
18385  AnsiString RearID = Track->TrackElementAt(1399, AVE.RearStartOrRepeatMins).ElementID;
18386  AnsiString FrontID = Track->TrackElementAt(1400, AVE.FrontStartOrRepeatDigits).ElementID;
18387  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << RearID << ' ' << FrontID << ' ' << AVE.OtherHeadCode << '\n';
18388  }
18389  if(AVE.FormatType == SNSShuttle) //should all have been converted to chr
18390  {
18391  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << ' ' << AVE.NonRepeatingShuttleLinkHeadCode << '\n';
18392  }
18393  if(AVE.FormatType == Repeat) //shouldn't be any repeats, only here to show if any have been copied
18394  {
18395  VecFile << Marker << "Repeat " << AVE.RearStartOrRepeatMins << ' ' << AVE.FrontStartOrRepeatDigits << ' ' << AVE.NumberOfRepeats << '\n';
18396  }
18397  if((AVE.FormatType == TimeCmd) || (AVE.FormatType == TimeCmdHeadCode) || (AVE.FormatType == FNSNonRepeatToShuttle) || (AVE.FormatType == FSHNewService))
18398  {
18399  TActionVectorEntry AVHolder = AVE;
18400  if(AVE.Command.SubString(1,3) == "chr")
18401  {
18402  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "sp")
18403  {
18404  AVE.Command = "Change of service to " + AVE.OtherHeadCode + " after split";
18405  AVE.OtherHeadCode = "";
18406  }
18407  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns")
18408  {
18409  AVE.Command = "Change of service to ";
18410  }
18411  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "Fns-sh")
18412  {
18413  AVE.Command = "Change to shuttle finishing service";
18414  }
18415  if(AVE.Command.SubString(5, AVE.Command.Length() - 4) == "F-nshs")
18416  {
18417  AVE.Command = "Change to shuttle service " + AVE.OtherHeadCode + " from feeder";
18418  AVE.OtherHeadCode = "";
18419  }
18420  }
18421  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << AVE.Command << ' ' << AVE.OtherHeadCode << '\n';
18422  AVE = AVHolder;
18423  }
18424  else if((AVE.FormatType == TimeLoc) && (AVE.ArrivalTime != TDateTime(-1)))
18425  {
18426  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << " Arr " << AVE.LocationName << '\n';
18427  }
18428  else if((AVE.FormatType == TimeLoc) && (AVE.DepartureTime != TDateTime(-1)))
18429  {
18430  VecFile << Marker << AnsiString(AVE.DepartureTime.TimeString()) << " Dep " << AVE.LocationName << '\n';
18431  }
18432  else if(AVE.FormatType == TimeTimeLoc)
18433  {
18434  VecFile << Marker << AnsiString(AVE.ArrivalTime.TimeString()) << ' ' << AnsiString(AVE.DepartureTime.TimeString()) << ' ' << AVE.LocationName << '\n';
18435  }
18436  else if(AVE.FormatType == PassTime)
18437  {
18438  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << ' ' << "Pass" << ' ' << AVE.LocationName << '\n';
18439  }
18440  else if(AVE.FormatType == ExitRailway) //ListOfExits added at v2.10.0
18441  {
18442  AnsiString ListOfExits = "";
18443  for(TNumListIterator NLIt = AVE.ExitList.begin(); NLIt != AVE.ExitList.end(); NLIt++)
18444  {
18445  ListOfExits += AnsiString(Track->TrackElementAt(1432, *NLIt).ElementID) + ' ';
18446  }
18447  VecFile << Marker << AnsiString(AVE.EventTime.TimeString()) << " Fer " << ListOfExits <<'\n';
18448  }
18449  else if(AVE.FormatType == FinRemHere)
18450  {
18451  VecFile << Marker << "Frh" << '\n';
18452  }
18453  }
18454  VecFile << '\n';
18455  }
18456  Utilities->CallLogPop(2318);
18457 }
18458 
18459 // ---------------------------------------------------------------------------
18460 
18461 TTrainDataEntry TTrainController::GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
18462 {
18463  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetServiceFromVector," + ServiceReference);
18464  FoundFlag = false;
18465  FinishType = true;
18466  for(unsigned int x = 0; x < Vector.size(); x++)
18467  {
18468  if(Vector.at(x).ServiceReference == ServiceReference)
18469  {
18470  if(Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1).FormatType == Repeat) //shouldn't be any repeats
18471  {
18472  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 2);
18473  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18474  {
18475  FinishType = false;
18476  }
18477  }
18478  else
18479  {
18480  TActionVectorEntry AVE = Vector.at(x).ActionVector.at(Vector.at(x).ActionVector.size() - 1);
18481  if((AVE.Command == "Fjo") || (AVE.Command == "Frh") || (AVE.Command == "Fer") || (AVE.Command == "Frh-sh"))
18482  {
18483  FinishType = false;
18484  }
18485  }
18486  FoundFlag = true;
18487  Utilities->CallLogPop(2319);
18488  return(Vector.at(x));
18489  }
18490  }
18491  Utilities->CallLogPop(2320);
18492  return(Vector.at(Vector.size() - 1)); //return last for want of returning something
18493 }
18494 
18495 // ---------------------------------------------------------------------------
18496 
18497 bool TTrainController::WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange) //times are "HH:MM"
18498 {
18499 //convert times to integer minutes
18500  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",WithinTimeRange," + Time1 + "," + Time2 + "," + AnsiString(MinuteRange));
18501  if((Time1 == "") || (Time2 == ""))
18502  {
18503  Utilities->CallLogPop(2213);
18504  return(false);
18505  }
18506  int Mins = Time1.SubString(4,2).ToInt();
18507  int Hours = Time1.SubString(1,2).ToInt();
18508  int Time1Mins = (Hours * 60) + Mins;
18509  Mins = Time2.SubString(4,2).ToInt();
18510  Hours = Time2.SubString(1,2).ToInt();
18511  int Time2Mins = (Hours * 60) + Mins;
18512  if(abs(Time1Mins - Time2Mins) <= MinuteRange)
18513  {
18514  Utilities->CallLogPop(2214);
18515  return(true);
18516  }
18517  Utilities->CallLogPop(2215);
18518  return(false);
18519 }
18520 
18521 // ---------------------------------------------------------------------------
18522 
18523 AnsiString TTrainController::ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival,
18524  bool &AnalysisError, int &MaxNumberOfSameDirections)
18525 {
18526  //input consists of services and service Arr or Dep times as a comma separated list, Location needed to determine direction information
18527 
18528  try
18529  {
18530  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTArrDep," + Input);
18531  AnsiString Output = "", OneService = "", TempStr1 = "", TempStr2 = "";
18532  int SCPos = 0;
18533  std::list<AnsiString> ServiceList; //this is the list of services with times extracted from Input - not to be confused with ServiceCallingPointsList
18534  //first change every second comma in Input to a semicolon so can separate services but keep times with services
18535  bool EvenComma = false;
18536  for(int x = 1; x <= Input.Length(); x++)
18537  {
18538  TempStr1 = Input[x];
18539  if(TempStr1 == AnsiString(',') && EvenComma)
18540  {
18541  TempStr2 += ';';
18542  }
18543  else
18544  {
18545  TempStr2 += Input[x];
18546  }
18547  if(TempStr1 == AnsiString(','))
18548  {
18549  EvenComma = !EvenComma;
18550  }
18551  }
18552  //load up the list of services with associated times
18553  while(TempStr2.Length() > 0)
18554  {
18555  SCPos = TempStr2.Pos(';');
18556  if(SCPos > 0) //0 if not found, as won't be when only one service left
18557  {
18558  OneService = TempStr2.SubString(1, SCPos - 1);
18559  ServiceList.push_back(OneService);
18560  TempStr2 = TempStr2.SubString(SCPos + 1, TempStr2.Length() - SCPos);
18561  }
18562  else //no semicolon so looking at last (or only) element
18563  {
18564  ServiceList.push_back(TempStr2);
18565  TempStr2 = "";
18566  }
18567  }
18568  ServiceList.sort(); // alphabetical order
18569  ServiceList.unique(); //remove duplicates
18570  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18571 
18572  //now add direction information from AllServiceCallingLocsMap - key is service ref and value a list of calling points in order
18573  int DirectionMarker = 0; //this is added in & runs from 1 upwards, same marker for diff services = same direction
18574  //first add the direction marker "&0" for not yet allocated - '&' is an identifier
18575  std::list<AnsiString>::iterator SLIt, SLIt1, SLIt2, SLIt3;
18576 
18577  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18578  {
18579  *SLIt = *SLIt + "&0"; //add in a basic direction marker to each service
18580  }
18581  SLIt3 = ServiceList.end();
18582  SLIt3--; //so points to last element
18583  AnsiString ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatInfo1, RepeatInfo2; //1 refers to first for..next loop & 2 to second
18584  int AmpersandPos, SpacePos, CommaPos1, CommaPos2, RepeatNum1, RepeatNum2;
18585  TAllServiceCallingLocsMap::iterator ASCLIt1, ASCLIt2;
18586  TServiceCallingLocsList ServiceCallingLocsList1, ServiceCallingLocsList2;
18587  MaxNumberOfSameDirections = 0; //at end of each SLIt loop if SameDirectionCount > MaxNumberOfSameDirections then MaxNumberOfSameDirections = SameDirectionCount
18588  int SameDirectionCount = 0; //starts at 1 at each SLIt loop (because SLIt1 entry already has a DirectionMarker) and increments for every same direction
18589 
18590  for(std::list<AnsiString>::iterator SLIt1 = ServiceList.begin(); SLIt1 != SLIt3; SLIt1++) //should be end() - 1 but can't use -1 with lists so have to improvise
18591  {
18592  SLIt = SLIt1;
18593  SLIt++; //so points to one after SLIt1
18594  if(SLIt1->SubString(SLIt1->Length() - 1, 2) != AnsiString("&0"))
18595  {
18596  continue; //already allocated so skip to the next
18597  }
18598  else
18599  {
18600  CommaPos1 = SLIt1->Pos(','); //can't be 0
18601  ServiceRef1 = SLIt1->SubString(1, CommaPos1 - 1);
18602  //but this contains "(First service..." etc so need to strip these, but use to extract RepeatNum
18603  SpacePos = ServiceRef1.Pos(' ');
18604  RepeatNum1 = 0;
18605  if(SpacePos > 0) //otherwise it's already correct
18606  {
18607  RepeatInfo1 = ServiceRef1.SubString(SpacePos + 2, ServiceRef1.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18608  ServiceRef1 = ServiceRef1.SubString(1, SpacePos - 1);
18609  if(RepeatInfo1[1] == 'F')
18610  {
18611  RepeatNum1 = 0;
18612  }
18613  else
18614  {
18615  SpacePos = RepeatInfo1.Pos(' ');
18616  RepeatNum1 = RepeatInfo1.SubString(SpacePos + 1, RepeatInfo1.Length() - SpacePos).ToInt();
18617  }
18618  }
18619  AnsiTime1 = SLIt1->SubString(CommaPos1 + 1, SLIt1->Length() - CommaPos1);
18620  //but this includes the "&0" etc so need to strip these
18621  AmpersandPos = AnsiTime1.Pos('&');
18622  AnsiTime1 = AnsiTime1.SubString(1, AmpersandPos - 1);
18623 
18624  ASCLIt1 = AllServiceCallingLocsMap.find(ServiceRef1);
18625  if(ASCLIt1 == AllServiceCallingLocsMap.end()) //can't find it
18626  {
18627  throw Exception("ASCLIt1 Error in " + Input);
18628  }
18629  ServiceCallingLocsList1 = ASCLIt1->second;
18630  AmpersandPos = SLIt1->Pos('&');
18631  *SLIt1 = SLIt1->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18632  *SLIt1 = *SLIt1 + AnsiString(++DirectionMarker); //now add the next marker (pre-increment), allow for it being more than one digit
18633 
18634  SameDirectionCount = 1;
18635  for(SLIt2 = SLIt; SLIt2 != ServiceList.end(); SLIt2++)
18636  {
18637  CommaPos2 = SLIt2->Pos(','); //can't be 0
18638  ServiceRef2 = SLIt2->SubString(1, CommaPos2 - 1);
18639  //but this contains "(First service..." etc so need to strip these
18640  SpacePos = ServiceRef2.Pos(' ');
18641  RepeatNum2 = 0;
18642  if(SpacePos > 0) //otherwise it's already correct
18643  {
18644  RepeatInfo2 = ServiceRef2.SubString(SpacePos + 2, ServiceRef2.Length() - SpacePos - 2); //drops the brackets and leaves "First service", "Repeat 2" etc
18645  ServiceRef2 = ServiceRef2.SubString(1, SpacePos - 1);
18646  if(RepeatInfo2[1] == 'F')
18647  {
18648  RepeatNum2 = 0;
18649  }
18650  else
18651  {
18652  SpacePos = RepeatInfo2.Pos(' ');
18653  RepeatNum2 = RepeatInfo2.SubString(SpacePos + 1, RepeatInfo2.Length() - SpacePos).ToInt();
18654  }
18655  }
18656  AnsiTime2 = SLIt2->SubString(CommaPos2 + 1, SLIt2->Length() - CommaPos2);
18657  //but this includes the "&0" etc so need to strip these
18658  AmpersandPos = AnsiTime2.Pos('&');
18659  AnsiTime2 = AnsiTime2.SubString(1, AmpersandPos - 1);
18660 
18661  ASCLIt2 = AllServiceCallingLocsMap.find(ServiceRef2);
18662  if(ASCLIt2 == AllServiceCallingLocsMap.end()) //can't find it
18663  {
18664  throw Exception("ASCLIt2 Error in " + Input);
18665  }
18666  ServiceCallingLocsList2 = ASCLIt2->second;
18667  //now compare the two
18668  if(SameDirection(0, ServiceRef1, ServiceRef2, AnsiTime1, AnsiTime2, RepeatNum1, RepeatNum2, ServiceCallingLocsList1, ServiceCallingLocsList2, Location, Arrival))
18669  {
18670  int AmpersandPos = SLIt2->Pos('&');
18671  *SLIt2 = SLIt2->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18672  *SLIt2 = *SLIt2 + AnsiString(DirectionMarker); //now add the same marker as *SLIt1
18673  SameDirectionCount++;
18674  }
18675  }
18676  if(SameDirectionCount > MaxNumberOfSameDirections)
18677  {
18678  MaxNumberOfSameDirections = SameDirectionCount;
18679  }
18680  }
18681  }
18682 
18683  if(SLIt3->SubString(SLIt3->Length() - 1, 2) == AnsiString("&0")) //*SLTIt3 is the last in the list and may not have been allocated, if not it doesn't match
18684  {
18685  //any existing direction so allocate it now
18686  AmpersandPos = SLIt3->Pos('&');
18687  *SLIt3 = SLIt3->SubString(1, AmpersandPos); //truncate up to & leave in the '&'
18688  *SLIt3 = *SLIt3 + AnsiString(++DirectionMarker);
18689  }
18690  //now change direction markers to upper case letters beginning with 'A' (and continuing with 'AA' if exceed 26) & add a comma before so have ServiceRef, DirectionMarker, Time
18691  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18692  {
18693  //extract the DirectionMarker as an integer
18694  AmpersandPos = SLIt->Pos('&');
18695  AnsiString DirectionMarkerString = SLIt->SubString(AmpersandPos + 1, SLIt->Length() - AmpersandPos); //extract the number as an ansistring
18696  AnsiString ServiceWithoutMarker = SLIt->SubString(1, AmpersandPos - 1); //truncate the &number
18697  DirectionMarker = DirectionMarkerString.ToInt();
18698  AnsiString DirectionSuffix = "";
18699  char c;
18700  if(DirectionMarker < 27)
18701  {
18702  c = 64 + DirectionMarker; //so 1 -> 'A'
18703  DirectionSuffix = "," + AnsiString(c);
18704  }
18705  else if(DirectionMarker < 53)
18706  {
18707  c = 65 + DirectionMarker - 27; //so 27 -> 'AA'
18708  DirectionSuffix = ",A" + AnsiString(c);
18709  }
18710  else
18711  {
18712  DirectionSuffix = ",?"; //shouldn'tn ever get this far!
18713  }
18714  *SLIt = ServiceWithoutMarker + DirectionSuffix;
18715  }
18716  //now prepare the final consolidated output
18717  for(SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18718  {
18719  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18720  }
18721  if(Output.Length() > 0)
18722  {
18723  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18724  }
18725  Utilities->CallLogPop(2216);
18726  return(Output);
18727  }
18728 
18729  catch(const Exception &e) //non error catch
18730  {
18731  AnalysisError = true;
18732  Utilities->CallLogPop(2227);
18733  return(e.Message);
18734  }
18735 }
18736 
18737 // ---------------------------------------------------------------------------
18738 
18739 AnsiString TTrainController::ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
18740 {
18741  //similar to above but doesn't include times in the input
18742  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",ConsolidateSARNTAtLoc," + Input);
18743  AnsiString InternalInput = Input, Output = "", OneService = "";
18744  int CommaPos = 0;
18745  std::list<AnsiString> ServiceList;
18746  //load up the list
18747  while(InternalInput.Length() > 0)
18748  {
18749  CommaPos = InternalInput.Pos(',');
18750  if(CommaPos > 0) //0 if not found, as won't be when only one service left
18751  {
18752  OneService = InternalInput.SubString(1, CommaPos - 1);
18753  ServiceList.push_back(OneService);
18754  InternalInput = InternalInput.SubString(CommaPos + 1, InternalInput.Length() - CommaPos);
18755  }
18756  else //no comma so looking at last (or only) element
18757  {
18758  ServiceList.push_back(InternalInput);
18759  InternalInput = "";
18760  }
18761  }
18762 
18763  ServiceList.sort(); // alphabetical order
18764  ServiceList.unique(); //remove duplicates
18765  NumTrainsAtLoc = ServiceList.size(); //calc this after removing duplicates as may not have changed
18766  for(std::list<AnsiString>::iterator SLIt = ServiceList.begin(); SLIt != ServiceList.end(); SLIt++)
18767  {
18768  Output = Output + *SLIt + ","; //will end up with an unwanted comma at the end
18769  }
18770  if(Output.Length() > 0)
18771  {
18772  Output = Output.SubString(1, Output.Length() - 1); //remove the last comma
18773  }
18774  Utilities->CallLogPop(2217);
18775  return(Output);
18776 }
18777 
18778 // ---------------------------------------------------------------------------
18779 
18780 
18781 bool TTrainController::SameDirection(int Caller, AnsiString Ref1In, AnsiString Ref2In, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1,
18782  TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
18783 {
18784  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SameDirection," + Ref1In + "," + Ref2In + "," + Time1 + "," + Time2 + "," +
18785  AnsiString(RepeatNum1) + "," + AnsiString(RepeatNum2) + "," + Location);
18786 
18787  std::list<AnsiString>::iterator LP1 = 0, LP2 = 0, ListPtr1 = 0, ListPtr2 = 0, LocPtr1 = 0, LocPtr2 = 0; //LP1 & 2 are temporary pointers, ListPtrs are
18788  //general list pointers, LocPtrs point to Location in the two lists
18789 
18790  //first find the relevant values for LocPtr1 & LocPtr2 taking account of cdts and times
18791  //for List1
18792  bool LocFound = false;
18793  AnsiString Ref1 = Ref1In, Ref2 = Ref2In;
18794  int IncMinutes;
18795  TDateTime FirstServiceTime;
18796 
18797  //first need to strip off /1, /2 etc if present from Ref1 & Ref2 (leave Ref1In & Ref2In for error message & retain value as target in finding the correct reference for cdts)
18798  int Ref1Target = 0, Ref1Count = 0;
18799  int SlashPos = Ref1.Pos('/');
18800  if(SlashPos > 0) //if 0 Ref1 == Ref1In & target stays at 0
18801  {
18802  Ref1Target = Ref1.SubString(SlashPos + 1, Ref1.Length() - SlashPos).ToInt();
18803  Ref1 = Ref1.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18804  }
18805  int Ref2Target = 0, Ref2Count = 0;
18806  SlashPos = Ref2.Pos('/');
18807  if(SlashPos > 0) //if 0 leave as is
18808  {
18809  Ref2Target = Ref2.SubString(SlashPos + 1, Ref2.Length() - SlashPos).ToInt();
18810  Ref2 = Ref2.SubString(1, SlashPos - 1); //truncate up to but omit '/'
18811  }
18812  for(ListPtr1 = List1.begin(); ListPtr1 != List1.end(); ListPtr1++) //note that when this routine entered Ref1In & Ref2In are already set to the correct services,
18813  {
18814  //even if others have same names. But if there are cdt's then need to refind the correct service
18815  if((*ListPtr1) == Location) //
18816  {
18817  LocPtr1 = ListPtr1; //may be modified later
18818  LocFound = true;
18819  }
18820  if(ListPtr1->SubString(1, 3) == "%%%")
18821  {
18822  AnsiString CDTTime = ListPtr1->SubString(4, 5);
18823  //now adjust the time to correspond to the repeat if there is one
18824  if(RepeatNum1 > 0) //if it is 0 then AnsiTime1 is already valid
18825  {
18826  IncMinutes = -1;
18827  FirstServiceTime = TDateTime(-1);
18828  bool BreakFlag = false;
18829  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18830  {
18831  if(TDVIt->ServiceReference == Ref1)
18832  {
18833  if(Ref1Target > Ref1Count)
18834  {
18835  Ref1Count++;
18836  continue;
18837  }
18838  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18839  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18840  {
18841  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18842  {
18843  FirstServiceTime = AVIt->EventTime; //i.e. the FirstService value of CDTTime
18844  BreakFlag = true;
18845  break;
18846  }
18847  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime) //add arr & dep in case find sooner (though dep shouldn't be sooner)
18848  {
18849  FirstServiceTime = AVIt->ArrivalTime;
18850  BreakFlag = true;
18851  break;
18852  }
18853  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18854  {
18855  FirstServiceTime = AVIt->DepartureTime;
18856  BreakFlag = true;
18857  break;
18858  }
18859  }
18860  if(BreakFlag)
18861  {
18862  break;
18863  }
18864  }
18865  }
18866  if(IncMinutes == -1)
18867  {
18868  throw Exception("Failed to find service for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18869  }
18870  if(FirstServiceTime == TDateTime(-1))
18871  {
18872  throw Exception("Failed to find first service time for ServiceRef1 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18873  }
18874  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(60, FirstServiceTime, RepeatNum1, IncMinutes));
18875  }
18876  if(!Arrival && (Time1 == CDTTime)) //continue if equal in case next is a departure for the Location
18877  {
18878  LocFound = false;
18879  continue;
18880  }
18881  if(Arrival && (Time1 == CDTTime)) //gone far enough so can stop
18882  {
18883  break;
18884  }
18885  if(Time1 > CDTTime) //not there yet so go on
18886  {
18887  LocFound = false;
18888  continue;
18889  }
18890  if(Time1 < CDTTime) //gone too far so can stop now
18891  {
18892  break;
18893  }
18894  }
18895  }
18896  if(!LocFound) //have to find it in both lists
18897  {
18898  Utilities->CallLogPop(2228);
18899  return( false);
18900  }
18901  //for List2
18902  LocFound = false;
18903  for(ListPtr2 = List2.begin(); ListPtr2 != List2.end(); ListPtr2++)
18904  {
18905  if((*ListPtr2) == Location)
18906  {
18907  LocPtr2 = ListPtr2; //may be modified later
18908  LocFound = true;
18909  }
18910  if(ListPtr2->SubString(1, 3) == "%%%")
18911  {
18912  AnsiString CDTTime = ListPtr2->SubString(4, 5);
18913  //now adjust the time to correspond to the repeat if there is one
18914  if(RepeatNum2 > 0) //if it is 0 then AnsiTime1 is already valid
18915  {
18916  IncMinutes = -1;
18917  FirstServiceTime = TDateTime(-1);
18918  bool BreakFlag = false;
18919  for(TTrainDataVector::iterator TDVIt = TrainDataVector.begin(); TDVIt != TrainDataVector.end(); TDVIt++)
18920  {
18921  if(TDVIt->ServiceReference == Ref2)
18922  {
18923  if(Ref2Target > Ref2Count)
18924  {
18925  Ref2Count++;
18926  continue;
18927  }
18928  IncMinutes = TDVIt->ActionVector.back().RearStartOrRepeatMins;
18929  for(TActionVector::iterator AVIt = TDVIt->ActionVector.begin(); AVIt != TDVIt->ActionVector.end(); AVIt++)
18930  {
18931  if(Utilities->Format96HHMM(AVIt->EventTime) == CDTTime)
18932  {
18933  FirstServiceTime = AVIt->EventTime;
18934  BreakFlag = true;
18935  break;
18936  }
18937  if(Utilities->Format96HHMM(AVIt->ArrivalTime) == CDTTime)
18938  {
18939  FirstServiceTime = AVIt->ArrivalTime;
18940  BreakFlag = true;
18941  break;
18942  }
18943  if(Utilities->Format96HHMM(AVIt->DepartureTime) == CDTTime)
18944  {
18945  FirstServiceTime = AVIt->DepartureTime;
18946  BreakFlag = true;
18947  break;
18948  }
18949  }
18950  if(BreakFlag)
18951  {
18952  break;
18953  }
18954  }
18955  }
18956  if(IncMinutes == -1)
18957  {
18958  throw Exception("IncMinutes -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18959  }
18960  if(FirstServiceTime == TDateTime(-1))
18961  {
18962  throw Exception("First service time -1 for ServiceRef2 in SameDirection " + Ref1In + " " + Ref2In + " " + Time1 + " " + Time2 + " " + AnsiString(RepeatNum1) + " " + AnsiString(RepeatNum2) + " " + Location);
18963  }
18964  CDTTime = Utilities->Format96HHMM(TrainController->GetRepeatTime(61, FirstServiceTime, RepeatNum2, IncMinutes));
18965  }
18966  if(!Arrival && (Time2 == CDTTime)) //continue if equal in case next is a departure for the Location
18967  {
18968  LocFound = false;
18969  continue;
18970  }
18971  if(Arrival && (Time2 == CDTTime)) //gone far enough so can stop
18972  {
18973  break;
18974  }
18975  if(Time2 > CDTTime) //not there yet so go on
18976  {
18977  LocFound = false;
18978  continue;
18979  }
18980  if(Time2 < CDTTime) //gone too far so can stop now
18981  {
18982  break;
18983  }
18984  }
18985  }
18986  if(!LocFound) //have to find it in both lists, and should be found but allow for it not being
18987  {
18988  Utilities->CallLogPop(2229);
18989  return( false);
18990  }
18991  //now, for the arrival analysis, see if there is a common location before the LocPtrs & within any cdts, and if so return true, else return false
18992  //set ListPtr1 to the search start position
18993  if(Arrival)
18994  {
18995  LP1 = List1.begin();
18996  LP1--; //now points to before the first entry
18997  for(ListPtr1 = LocPtr1; ListPtr1 != LP1; ListPtr1--) //search backwards from Location
18998  {
18999  if(ListPtr1 == List1.begin())
19000  {
19001  break;
19002  }
19003  if(ListPtr1->SubString(1, 3) == "%%%") //a cdt event
19004  {
19005  ListPtr1++; //point to one past the cdt
19006  break;
19007  }
19008  }
19009  //set ListPtr2 to the search start position
19010  LP2 = List2.begin();
19011  LP2--; //now points to before the first entry
19012  for(ListPtr2 = LocPtr2; ListPtr2 != LP2; ListPtr2--)
19013  {
19014  if(ListPtr2 == List2.begin())
19015  {
19016  break;
19017  }
19018  if(ListPtr2->SubString(1, 3) == "%%%") //a cdt event
19019  {
19020  ListPtr2++; //point to one past the cdt
19021  break;
19022  }
19023  }
19024  //ListPtr1 & 2 now at search start position
19025  LP1 = ListPtr1;
19026  LP2 = ListPtr2;
19027  //now search forwards, i.e. for common locations before Location
19028  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19029  {
19030  if(ListPtr1 == LocPtr1) //reached Location without finding a common earlier location so skip to the backwards check
19031  {
19032  break;
19033  }
19034  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location
19035  {
19036  break;
19037  }
19038  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19039  {
19040  if(ListPtr2 == LocPtr2) //not found common earlier location so go to the next ListPtr1
19041  {
19042  break;
19043  }
19044  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common earlier location so go to the next ListPtr1
19045  {
19046  break;
19047  }
19048  if((*ListPtr1) == (*ListPtr2)) //found a common earlier location
19049  {
19050  Utilities->CallLogPop(2230);
19051  return( true);
19052  }
19053  }
19054  }
19055  }
19056 
19057  //now, for the departure analysis, reset the start positions and search locations after Location
19058 
19059  else
19060  {
19061  LP1 = LocPtr1;
19062  LP1++; //start at one past the location itself
19063  LP2 = LocPtr2;
19064  LP2++;
19065  for(ListPtr1 = LP1; ListPtr1 != List1.end(); ListPtr1++)
19066  {
19067  if(ListPtr1 == List1.end()) //reached end point so stop
19068  {
19069  break;
19070  }
19071  if(ListPtr1->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location
19072  {
19073  break;
19074  }
19075  for(ListPtr2 = LP2; ListPtr2 != List2.end(); ListPtr2++)
19076  {
19077  if(ListPtr2 == List2.end()) //reached end point so go to next ListPtr1
19078  {
19079  break;
19080  }
19081  if(ListPtr2->SubString(1, 3) == "%%%") //reached a cdt event without finding a common location so go to the next ListPtr1
19082  {
19083  break;
19084  }
19085  if((*ListPtr1) == (*ListPtr2)) //found a common later location
19086  {
19087  Utilities->CallLogPop(2231);
19088  return( true);
19089  }
19090  }
19091  }
19092  }
19093  Utilities->CallLogPop(2232);
19094  return( false);
19095 }
19096 
19097 // ---------------------------------------------------------------------------
19098 
19099 AnsiString TTrainController::GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
19100 {
19101  // changed at v2.7.0 to show allowable exit elements
19102  if(ExitList.empty())
19103  {
19104  return("");
19105  }
19106  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",GetExitLocationAndAt");
19107  AnsiString StartName = Track->TrackElementAt(735, *(ExitList.begin())).ActiveTrackElementName;
19108  AnsiString ExitLocList = "";
19109  AllowedExits = "";
19110 
19111  unsigned int Counter = 0;
19112  for(TNumListIterator ELIt = ExitList.begin(); ELIt != ExitList.end(); ELIt++)
19113  {
19114  ExitLocList += Track->TrackElementAt(1018, *ELIt).ElementID + " ";
19115  Counter++;
19116  if(((Counter % 6) == 0) && (Counter < (ExitList.size() - 1))) // only add a newline if more to come
19117  {
19118  ExitLocList += "\n";
19119  }
19120  }
19121  if(StartName == "")
19122  {
19123  if(ExitList.size() == 1)
19124  {
19125  AnsiString ID = Track->TrackElementAt(738, *(ExitList.begin())).ElementID;
19126  Utilities->CallLogPop(1571);
19127  return(" at " + ID);
19128  }
19129  else
19130  {
19131  Utilities->CallLogPop(1572);
19132  if(ExitList.size() < 4)
19133  {
19134  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19135  return("");
19136  }
19137  else
19138  {
19139  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19140  return("");
19141  }
19142  }
19143  }
19144  for(TNumListIterator ELIT = ExitList.begin(); ELIT != ExitList.end(); ELIT++)
19145  {
19146  if(Track->TrackElementAt(736, *ELIT).ActiveTrackElementName != StartName)
19147  {
19148  Utilities->CallLogPop(1570);
19149  if(ExitList.size() < 4)
19150  {
19151  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19152  return("");
19153  }
19154  else
19155  {
19156  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19157  return("");
19158  }
19159  }
19160  }
19161  Utilities->CallLogPop(1569);
19162  if(ExitList.size() < 4)
19163  {
19164  AllowedExits = ",\nallowable exit element(s): " + ExitLocList;
19165  return(" at " + StartName);
19166  }
19167  else
19168  {
19169  AllowedExits = ",\nallowable exit element(s):\n" + ExitLocList;
19170  return(" at " + StartName);
19171  }
19172 }
19173 
19174 // ---------------------------------------------------------------------------
19175 /* can't trust this as locations within a vector may not be contiguous
19176  bool TTrainController::IsServiceTerminating(int Caller, TTrainDataEntry *TDEPtr, TActionVectorEntry *AVPtr)
19177  {
19178  //Search ActionVector from the position after the entry value for Ptr to the end, and return true if find a Finish
19179  //entry before Fer or TimeLoc. No point checking for TimeTimeLoc since at a stop location now so a later TimeTimeLoc
19180  //must be preceded by a TimeLoc departure
19181  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",IsServiceTerminating");
19182  for(unsigned int x=1;x<TDEPtr->ActionVector.size();x++)
19183  {
19184  if((AVPtr + x) < TDEPtr->ActionVector.end())
19185  {
19186  AnsiString xx = (AVPtr + x)->Command;//test
19187  TTimetableFormatType xy = (AVPtr + x)->FormatType;//test
19188  TTimetableSequenceType xz = (AVPtr + x)->SequenceType;//test
19189  if(((AVPtr + x)->Command == "Fer") || ((AVPtr + x)->FormatType == TimeLoc))
19190  {
19191  Utilities->CallLogPop();
19192  return false;
19193  }
19194  else if((AVPtr + x)->SequenceType == Finish)
19195  {
19196  Utilities->CallLogPop();
19197  return true;
19198  }
19199  }
19200  }
19201  Utilities->CallLogPop();
19202  return false;
19203  }
19204 */
19205 // ---------------------------------------------------------------------------
19206 
19207 void TTrainController::SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
19208 {
19209  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SendPerformanceSummary");
19210  AnsiString FormatStr = "####0.0";
19211  AnsiString AvLateArrMins = "";
19212  AnsiString AvEarlyArrMins = "";
19213  AnsiString AvLatePassMins = "";
19214  AnsiString AvEarlyPassMins = "";
19215  AnsiString AvLateDepMins = "";
19216  AnsiString AvLateExitMins = "";
19217  AnsiString AvEarlyExitMins = "";
19218 
19219  if(LateArrivals > 0)
19220  {
19221  AvLateArrMins = FormatFloat(FormatStr, (TotLateArrMins / LateArrivals));
19222  }
19223  if(EarlyArrivals > 0)
19224  {
19225  AvEarlyArrMins = FormatFloat(FormatStr, (TotEarlyArrMins / EarlyArrivals));
19226  }
19227  if(LatePasses > 0)
19228  {
19229  AvLatePassMins = FormatFloat(FormatStr, (TotLatePassMins / LatePasses));
19230  }
19231  if(EarlyPasses > 0)
19232  {
19233  AvEarlyPassMins = FormatFloat(FormatStr, (TotEarlyPassMins / EarlyPasses));
19234  }
19235  if(LateDeps > 0)
19236  {
19237  AvLateDepMins = FormatFloat(FormatStr, (TotLateDepMins / LateDeps));
19238  }
19239  if(LateExits > 0) //added at v2.9.1
19240  {
19241  AvLateExitMins = FormatFloat(FormatStr, (TotLateExitMins / LateExits));
19242  }
19243  if(EarlyExits > 0) //added at v2.9.1
19244  {
19245  AvEarlyExitMins = FormatFloat(FormatStr, (TotEarlyExitMins / EarlyExits));
19246  }
19247  PerfFile << '\n' << '\n' << "***************************************";
19248  PerfFile << '\n' << '\n' << "Performance summary:" << '\n';
19249 
19250  if(OnTimeArrivals != 1)
19251  {
19252  PerfFile << OnTimeArrivals << " on-time arrivals" << '\n';
19253  }
19254  else
19255  {
19256  PerfFile << OnTimeArrivals << " on-time arrival" << '\n';
19257  }
19258  if(LateArrivals > 1)
19259  {
19260  PerfFile << LateArrivals << " late arrivals (average " << AvLateArrMins.c_str() << " min)" << '\n';
19261  }
19262  else if(LateArrivals == 1)
19263  {
19264  PerfFile << LateArrivals << " late arrival (" << AvLateArrMins.c_str() << " min)" << '\n';
19265  }
19266  else
19267  {
19268  PerfFile << LateArrivals << " late arrivals" << '\n';
19269  }
19270  if(EarlyArrivals > 1)
19271  {
19272  PerfFile << EarlyArrivals << " early arrivals (average " << AvEarlyArrMins.c_str() << " min)" << '\n';
19273  }
19274  else if(EarlyArrivals == 1)
19275  {
19276  PerfFile << EarlyArrivals << " early arrival (" << AvEarlyArrMins.c_str() << " min)" << '\n';
19277  }
19278  else
19279  {
19280  PerfFile << EarlyArrivals << " early arrivals" << '\n';
19281  }
19282  if(OnTimePasses != 1)
19283  {
19284  PerfFile << OnTimePasses << " on-time passes" << '\n';
19285  }
19286  else
19287  {
19288  PerfFile << OnTimePasses << " on-time pass" << '\n';
19289  }
19290  if(LatePasses > 1)
19291  {
19292  PerfFile << LatePasses << " late passes (average " << AvLatePassMins.c_str() << " min)" << '\n';
19293  }
19294  else if(LatePasses == 1)
19295  {
19296  PerfFile << LatePasses << " late pass (" << AvLatePassMins.c_str() << " min)" << '\n';
19297  }
19298  else
19299  {
19300  PerfFile << LatePasses << " late passes" << '\n';
19301  }
19302  if(EarlyPasses > 1)
19303  {
19304  PerfFile << EarlyPasses << " early passes (average " << AvEarlyPassMins.c_str() << " min)" << '\n';
19305  }
19306  else if(EarlyPasses == 1)
19307  {
19308  PerfFile << EarlyPasses << " early pass (" << AvEarlyPassMins.c_str() << " min)" << '\n';
19309  }
19310  else
19311  {
19312  PerfFile << EarlyPasses << " early passes" << '\n';
19313  }
19314 
19315  if(OnTimeExits != 1) //this batch added at v2.9.1
19316  {
19317  PerfFile << OnTimeExits << " on-time exits" << '\n';
19318  }
19319  else
19320  {
19321  PerfFile << OnTimeExits << " on-time exit" << '\n';
19322  }
19323  if(LateExits > 1)
19324  {
19325  PerfFile << LateExits << " late exits (average " << AvLateExitMins.c_str() << " min)" << '\n';
19326  }
19327  else if(LateExits == 1)
19328  {
19329  PerfFile << LateExits << " late exit (" << AvLateExitMins.c_str() << " min)" << '\n';
19330  }
19331  else
19332  {
19333  PerfFile << LateExits << " late exits" << '\n';
19334  }
19335  if(EarlyExits > 1)
19336  {
19337  PerfFile << EarlyExits << " early exits (average " << AvEarlyExitMins.c_str() << " min)" << '\n';
19338  }
19339  else if(EarlyExits == 1)
19340  {
19341  PerfFile << EarlyExits << " early exit (" << AvEarlyExitMins.c_str() << " min)" << '\n';
19342  }
19343  else
19344  {
19345  PerfFile << EarlyExits << " early exits" << '\n';
19346  }
19347 
19348  if(OnTimeDeps != 1)
19349  {
19350  PerfFile << OnTimeDeps << " on-time departures" << '\n';
19351  }
19352  else
19353  {
19354  PerfFile << OnTimeDeps << " on-time departure" << '\n';
19355  }
19356  if(LateDeps > 1)
19357  {
19358  PerfFile << LateDeps << " late departures (average " << AvLateDepMins.c_str() << " min)" << '\n';
19359  }
19360  else if(LateDeps == 1)
19361  {
19362  PerfFile << LateDeps << " late departure (" << AvLateDepMins.c_str() << " min)" << '\n';
19363  }
19364  else
19365  {
19366  PerfFile << LateDeps << " late departures" << '\n';
19367  }
19368  TDateTime TempExcessLCDownTime;
19369  for(unsigned int x = 0; x < Track->BarriersDownVector.size(); x++) //added at v2.6.0 - should have been added earlier
19370  {
19371 // if(Track->BarriersDownVector.at(x).ReducedTimePenalty) //assume train still to cross LC as probably will, else have false high value & can have
19372  //later perf summaries with lower values, changed at v2.8.0
19373 // {
19374  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime - TDateTime(180.0 / 86400);
19375 // }
19376 /*
19377  else
19378  {
19379  TempExcessLCDownTime = TrainController->TTClockTime - Track->BarriersDownVector.at(x).StartTime;
19380  }
19381 */
19382  if(TempExcessLCDownTime > TDateTime(0))
19383  {
19384  TrainController->ExcessLCDownMins += (double(TempExcessLCDownTime) * 1440);
19385  }
19386  }
19387 
19388  AnsiString FormattedExcessLCDownMins = FormatFloat(FormatStr, ExcessLCDownMins);
19389 
19390  if(ExcessLCDownMins > 0.1)
19391  {
19392  PerfFile << FormattedExcessLCDownMins.c_str() << " excess minutes of level crossing barrier down time" << '\n';
19393  }
19394  if(MissedStops != 1)
19395  {
19396  PerfFile << MissedStops << " missed stops" << '\n';
19397  }
19398  else
19399  {
19400  PerfFile << MissedStops << " missed stop" << '\n';
19401  }
19402  if(OtherMissedEvents != 1)
19403  {
19404  PerfFile << OtherMissedEvents << " other missed events" << '\n';
19405  }
19406  else
19407  {
19408  PerfFile << OtherMissedEvents << " other missed event" << '\n';
19409  }
19410  if(SkippedTTEvents != 1)
19411  {
19412  PerfFile << SkippedTTEvents << " skipped timetable events" << '\n';
19413  }
19414  else
19415  {
19416  PerfFile << SkippedTTEvents << " skipped timetable event" << '\n';
19417  }
19418  if(UnexpectedExits != 1)
19419  {
19420  PerfFile << UnexpectedExits << " unexpected train exits" << '\n';
19421  }
19422  else
19423  {
19424  PerfFile << UnexpectedExits << " unexpected train exit" << '\n';
19425  }
19426  if(IncorrectExits != 1)
19427  {
19428  PerfFile << IncorrectExits << " incorrect train exits" << '\n';
19429  }
19430  else
19431  {
19432  PerfFile << IncorrectExits << " incorrect train exit" << '\n';
19433  }
19434  if(NumFailures != 1)
19435  {
19436  PerfFile << NumFailures << " train failures" << '\n';
19437  }
19438  else
19439  {
19440  PerfFile << NumFailures << " train failure" << '\n';
19441  }
19442  if(AvHoursIntValue > 0)
19443  {
19444  if(AvHoursIntValue == 1)
19445  {
19446  PerfFile << AvHoursIntValue << " hour mean time betweeen train failures" << '\n';
19447  }
19448  else
19449  {
19450  PerfFile << AvHoursIntValue << " hours mean time betweeen train failures" << '\n';
19451  }
19452  }
19453  AnsiString AvLateMinsLocsNotReached = "";
19454 
19456  int LocsNotReached = (NotStartedTrainLateArr + OperatingTrainLateArr); //dropped divide by 2 after 2.7.0 as don't count late departures as 'failed to arrive'
19457  // each location has an arrival and departure (generally) so divide by 2 - no, dropped after 2.7.0
19458 
19459  if(LocsNotReached > 0)
19460  {
19461  AvLateMinsLocsNotReached = FormatFloat(FormatStr, (OperatingTrainLateMins + NotStartedTrainLateMins) / (NotStartedTrainLateArr + OperatingTrainLateArr));
19462  PerfFile << LocsNotReached << " locations that trains failed to reach (average lateness " << AvLateMinsLocsNotReached.c_str() << " min)" << '\n';
19463  }
19464  if(SPADRisks != 1)
19465  {
19466  PerfFile << SPADRisks << " SPAD risks" << '\n';
19467  }
19468  else
19469  {
19470  PerfFile << SPADRisks << " SPAD risk" << '\n';
19471  }
19472  if(SPADEvents != 1)
19473  {
19474  PerfFile << SPADEvents << " SPADs" << '\n';
19475  }
19476  else
19477  {
19478  PerfFile << SPADEvents << " SPAD" << '\n';
19479  }
19480  if(Derailments != 1)
19481  {
19482  PerfFile << Derailments << " derailments" << '\n';
19483  }
19484  else
19485  {
19486  PerfFile << Derailments << " derailment" << '\n';
19487  }
19488  if(CrashedTrains != 1)
19489  {
19490  PerfFile << CrashedTrains << " crashed trains" << '\n';
19491  }
19492  else
19493  {
19494  PerfFile << CrashedTrains << " crashed train" << '\n';
19495  }
19496  PerfFile << '\n' << "***************************************" << '\n';
19497 
19498  bool DerailSPADFlag = false, CrashFlag = false;
19499 
19500  int OverallScorePercent = 100;
19501  int TotArrDepExit = 0;
19502  double TotLateMinsFactor = 1;
19503  double MissedStopAndSPADRiskFactor = 1;
19504  double NetNegFactor = 1;
19505 
19507  EarlyExits + LateExits + OnTimeExits; //exits added at v2.9.1, passes not counted
19508  // TotArrDep: total number of arrivals & departures including those for trains that haven't reached their destinations yet and are late
19509  // changed at v1.1.4 - calc was inside "if(OverallScorePercent == 100).." block so could remain 0 for SPADs & crashes, & then received the
19510  // 'no timetabled departures... message, which was inappropriate
19511 
19512  if((SPADEvents > 0) || (Derailments > 0))
19513  {
19514  OverallScorePercent = 5; // overrides other calculations
19515  DerailSPADFlag = true;
19516  }
19517  if(CrashedTrains > 0)
19518  {
19519  OverallScorePercent = 0; // overrides other calculations
19520  CrashFlag = true;
19521  }
19522  if(OverallScorePercent == 100)
19523  {
19524  if(TotArrDepExit > 0)
19525  {
19526  TotLateMinsFactor =
19528  ((OtherMissedEvents + SkippedTTEvents + UnexpectedExits + ExcessLCDownMins) * 15)) / TotArrDepExit); //exits added at v2.9.1
19529  // TotLateMinsFactor: negative exponential factor based on overall average arr & dep minutes late (with OtherMissedEvents & UnexpectedExits
19530  // counting as 15 mins late each), where 4 mins late average = half, 8 mins late = a quarter etc
19531  MissedStopAndSPADRiskFactor = exp((-17.33) * (MissedStops + SPADRisks + IncorrectExits) / TotArrDepExit);
19532  // MissedEventAndSPADRiskFactor: negative exponential factor based on number of missed stops, SPAD risks & IncorrectExits as a proportion
19533  // of arrivals & departures, where 4% = half, 8% = a quarter etc
19534  NetNegFactor = TotLateMinsFactor * MissedStopAndSPADRiskFactor;
19535  // NetNegfactor: product of the above two
19536  OverallScorePercent = 100 * NetNegFactor;
19537  }
19538  }
19539  if((TotArrDepExit > 0) || DerailSPADFlag || CrashFlag)
19540  // flag condits added at v1.1.4 - see above for what the error was
19541  {
19542  AnsiString OneFailureString = ", though the failure would account for some poor performance";
19543  AnsiString TwoOrMoreFailureString = ", though the failures would account for some poor performance";
19544  AnsiString AddedString = "";
19545  if(NumFailures == 1)
19546  {
19547  AddedString = OneFailureString;
19548  }
19549  if(NumFailures > 1)
19550  {
19551  AddedString = TwoOrMoreFailureString;
19552  }
19553  PerfFile << "\nOverall score: " << OverallScorePercent << "%\n";
19554  AnsiString Rating = "";
19555  if(OverallScorePercent == 100)
19556  {
19557  Rating = "Perfect!";
19558  }
19559  else if(OverallScorePercent >= 95)
19560  {
19561  Rating = "Excellent";
19562  }
19563  else if(OverallScorePercent >= 90)
19564  {
19565  Rating = "Very good";
19566  }
19567  else if(OverallScorePercent >= 80)
19568  {
19569  Rating = "Good";
19570  }
19571  else if(OverallScorePercent >= 70)
19572  {
19573  Rating = "Fair";
19574  }
19575  else if(OverallScorePercent >= 60)
19576  {
19577  Rating = "Unacceptable" + AddedString;
19578  }
19579  else if(OverallScorePercent >= 50)
19580  {
19581  Rating = "Poor" + AddedString;
19582  }
19583  else if(OverallScorePercent >= 40)
19584  {
19585  Rating = "Bad" + AddedString;
19586  }
19587  else if(OverallScorePercent >= 30)
19588  {
19589  Rating = "Very bad" + AddedString;
19590  }
19591  else if(OverallScorePercent >= 20)
19592  {
19593  Rating = "Terrible" + AddedString;
19594  }
19595  else if(OverallScorePercent >= 10)
19596  {
19597  Rating = "Appalling" + AddedString;
19598  }
19599  else if(OverallScorePercent >= 5)
19600  {
19601  if(DerailSPADFlag)
19602  {
19603  Rating = "Disastrous - potential loss of life";
19604  }
19605  // SPADs/Derailments
19606  else
19607  {
19608  Rating = "Dire" + AddedString;
19609  }
19610  }
19611  else if(OverallScorePercent < 5)
19612  {
19613  if(CrashFlag)
19614  {
19615  Rating = "Catastrophic - loss of life"; // Crashes
19616  }
19617  else
19618  {
19619  Rating = "Abysmal";
19620  }
19621  }
19622  PerfFile << "Overall rating: " << Rating.c_str() << '\n';
19623  }
19624  else
19625  {
19626  PerfFile << "\nThere were no timetabled departures, arrivals or exits so there is insufficient information to provide a performance score or rating" << '\n';
19627  }
19628  PerfFile << '\n' << "***************************************";
19629  Utilities->CallLogPop(1736);
19630 }
19631 
19632 // ---------------------------------------------------------------------------
19633 
19635 {
19636  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",SetWarningFlags");
19637  for(unsigned int x = 0; x < TrainVector.size(); x++)
19638  {
19639  TTrain &Train = TrainVectorAt(58, x);
19640  if(Train.Crashed)
19641  // can't use background colours for crashed & derailed because same colour
19642  {
19643  CrashWarning = true;
19644  }
19645  else if(Train.Derailed)
19646  // can't use background colours for crashed & derailed because same colour
19647  {
19648  DerailWarning = true;
19649  }
19650  else if(Train.BackgroundColour == clSPADBackground)
19651  // use colour as that changes as soon as passes signal
19652  {
19653  SPADWarning = true;
19654  }
19655  else if(Train.BackgroundColour == clTrainFailedBackground)
19656  {
19657  TrainFailedWarning = true;
19658  }
19659  else if(Train.BackgroundColour == clCallOnBackground)
19660  // use colour as also stopped at signal
19661  {
19662  CallOnWarning = true;
19663  }
19664  else if(Train.BackgroundColour == clSignalStopBackground)
19665  // use colour to distinguish from call-on
19666  {
19667  SignalStopWarning = true;
19668  }
19669  else if(Train.BackgroundColour == clBufferAttentionNeeded)
19670  // use colour to distinguish from ordinary buffer stop
19671  {
19672  BufferAttentionWarning = true;
19673  }
19674  }
19675  Utilities->CallLogPop(1796);
19676 }
19677 
19678 // ---------------------------------------------------------------------------
19679 
19681 {
19682  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",CalcSignalStopLateness");
19683 
19684  // calculate lateness for running trains
19687  for(unsigned int x = 0; x < TrainVector.size(); x++)
19688  {
19689  TTrain &Train = TrainVectorAt(64, x);
19690  for(TActionVectorEntry * AVEntryPtr = &Train.TrainDataEntryPtr->ActionVector.front(); AVEntryPtr < &Train.TrainDataEntryPtr->ActionVector.back();
19691  AVEntryPtr++)
19692  {
19693  if(AVEntryPtr < Train.ActionVectorEntryPtr)
19694  {
19695  continue;
19696  }
19697  if((AVEntryPtr->ArrivalTime > TDateTime(-1)) && !Train.RevisedStoppedAtLoc() && (GetRepeatTime(42, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19698  TTClockTime))
19699  {
19700  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(43, AVEntryPtr->ArrivalTime, Train.RepeatNumber, Train.IncrementalMinutes));
19702  }
19703 /* dropped departures after 2.7.0 because these don't count for 'failed to reach' numbers
19704  if((AVEntryPtr->DepartureTime > TDateTime(-1)) && (GetRepeatTime(44, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes) <
19705  TTClockTime))
19706  {
19707  OperatingTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(45, AVEntryPtr->DepartureTime, Train.RepeatNumber, Train.IncrementalMinutes));
19708  OperatingTrainArrDep++;
19709  }
19710 */
19711  }
19712  }
19713 
19714  // calculate lateness for trains that haven't started yet (could be held awaiting entry)
19717 
19718  for(unsigned int x = 0; x < TrainDataVector.size(); x++)
19719  {
19720  TTrainDataEntry & TDEntry = TrainDataVector.at(x);
19721  const TActionVectorEntry &AVEntryLast = TDEntry.ActionVector.at(TDEntry.ActionVector.size() - 1);
19722  int IncrementalMinutes = 0;
19723  if(AVEntryLast.FormatType == Repeat)
19724  {
19725  IncrementalMinutes = AVEntryLast.RearStartOrRepeatMins;
19726  }
19727  for(int y = 0; y < TDEntry.NumberOfTrains; y++)
19728  {
19729  TTrainOperatingData &TTOD = TDEntry.TrainOperatingDataVector.at(y);
19730  if(TTOD.RunningEntry != NotStarted)
19731  {
19732  continue;
19733  }
19734  // note that can't rely on the above for sessionfiles saved before v0.6b as wasn't set to Running for Sns/Fsp/rsp & shuttles
19735  // but if trains had exited then would be set to Exited, so need to check against trains still operating - use the test below
19736  bool TrainOperatingFlag = false;
19737  for(unsigned int a = 0; a < TrainController->TrainVector.size(); a++)
19738  {
19739  if((TrainController->TrainVector.at(a).TrainDataEntryPtr == &TDEntry) && (TrainController->TrainVector.at(a).RepeatNumber == y))
19740  {
19741  TrainOperatingFlag = true;
19742  break;
19743  }
19744  }
19745  if(TrainOperatingFlag)
19746  {
19747  continue;
19748  }
19749  if(GetRepeatTime(46, TDEntry.ActionVector.at(0).EventTime, y, IncrementalMinutes) > TTClockTime)
19750  {
19751  break; // if the first time is greater than TTClockTime then all the rest will also be greater (& default of -1 will be less so will be ignored)
19752  }
19753  for(unsigned int z = 0; z < TDEntry.ActionVector.size(); z++)
19754  {
19755  TActionVectorEntry &AVEntry = TDEntry.ActionVector.at(z);
19756  if(GetRepeatTime(35, AVEntry.EventTime, y, IncrementalMinutes) > TTClockTime)
19757  {
19758  break; // all the rest will also be greater (& default of -1 will be less)
19759  }
19760  if(GetRepeatTime(36, AVEntry.ArrivalTime, y, IncrementalMinutes) > TTClockTime)
19761  {
19762  break; // all the rest will also be greater (& default of -1 will be less)
19763  }
19764  if(GetRepeatTime(37, AVEntry.DepartureTime, y, IncrementalMinutes) > TTClockTime)
19765  {
19766  break; // all the rest will also be greater (& default of -1 will be less)
19767  }
19768  if((AVEntry.ArrivalTime > TDateTime(-1)) && (GetRepeatTime(38, AVEntry.ArrivalTime, y, IncrementalMinutes) < TTClockTime))
19769  {
19770  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(39, AVEntry.ArrivalTime, y, IncrementalMinutes));
19772  }
19773 /* dropped departures after 2.7.0 as only interested in 'failed to reach' number - if train hasn't arrived then it hasn't departed so shouldn't count that as part of 'failed to reach'
19774  if((AVEntry.DepartureTime > TDateTime(-1)) && (GetRepeatTime(40, AVEntry.DepartureTime, y, IncrementalMinutes) < TTClockTime))
19775  {
19776  NotStartedTrainLateMins += 1440 * double(TTClockTime - GetRepeatTime(41, AVEntry.DepartureTime, y, IncrementalMinutes));
19777  NotStartedTrainArrDep++;
19778  }
19779 */
19780  }
19781  }
19782  }
19783  Utilities->CallLogPop(1894);
19784 }
19785 
19786 // ---------------------------------------------------------------------------
19787 
19789 // new v2.2.0 for OperatorActionPanel
19790 // clears entries then adds values for running trains then for continuation entries
19791 // dont limit size here as need to check all trains (OAListBox is limited to 20 trains in Interface.cpp)
19792 {
19793  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildOpTimeToActMultimap");
19794  OpTimeToActMultiMap.clear();
19795  TOpTimeToActMultiMapEntry OpTimeToActMultiMapEntry;
19796 
19797  if(!TrainVector.empty())
19798  // build OpTimeToActMultiMap entries for running trains
19799  {
19800  AnsiString HeadCode;
19801  // dropped in favour of TrainID for running trains int VecPos; //TrackVectorPosition of LeadElement or continuation where train is to enter
19802  int TrainID;
19803  THCandTrainPosParam HCandTrainPosParam;
19804  for(unsigned int x = 0; x < TrainVector.size(); x++)
19805  {
19806  HeadCode = TrainVectorAt(62, x).HeadCode;
19807  TrainID = TrainVectorAt(63, x).TrainID;
19808  HCandTrainPosParam.first = HeadCode;
19809  HCandTrainPosParam.second = TrainID;
19810  float TimeToAct = TrainVectorAt(65, x).OpTimeToAct;
19811  if((TimeToAct >= 0) && (TimeToAct < 59.9))
19812  // -1 indicates don't display
19813  {
19814  OpTimeToActMultiMapEntry.first = TimeToAct;
19815  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19816  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19817  }
19818  }
19819  }
19820 /*
19821  * class TContinuationTrainExpectationEntry
19822  {
19823  public:
19824  AnsiString Description; ///< service description
19825  AnsiString HeadCode; ///< service headcode
19826  int RepeatNumber; ///< service RepeatNumber
19827  int IncrementalMinutes; ///< Repeat separation in minutes
19828  int IncrementalDigits; ///< Repeat headcode separation
19829  int VectorPosition; ///< TrackVectorPosition for the continuation element
19830  TTrainDataEntry *TrainDataEntryPtr; ///< points to the service entry in the timetable's TrainDataVector
19831  };
19832 
19833  Multimap class for TContinuationTrainExpectationEntry objects, where the access key is the expectation time
19834  typedef std::multimap<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMap;
19835  typedef TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator; ///< iterator for the multimap
19836  typedef std::pair<TDateTime, TContinuationTrainExpectationEntry> TContinuationTrainExpectationMultiMapPair; ///< a single multimap entry
19837 */
19838 
19840  // build OpTimeToActMultiMap entries for expected trains
19841  {
19842  // note that using the ContinuationTrainExpectationMultiMap automatically ensures that entries are in ascending time order
19843  // first have to calculate times to red signal for each train due to enter (ignore later trains as will likely change before they are due)
19844  float TimeToAct = 0; // minutes
19845  int DistanceToRedSignal = 0; // metres
19846  TContinuationEntryVecPosVector ContinuationEntryVecPosVector;
19847  // used to ensure only one train displayed for a given continuation
19848  ContinuationEntryVecPosVector.clear();
19849  bool LaterTrain = false;
19852  {
19853  LaterTrain = false;
19854  if(CTEIt->second.TrainDataEntryPtr->TrainOperatingDataVector.at(CTEIt->second.RepeatNumber).RunningEntry != NotStarted)
19855  {
19856  CTEIt++;
19857  continue; // not interested in running or exited trains
19858  }
19859  if(Track->TrackElementAt(934, CTEIt->second.VectorPosition).TrainIDOnElement > 0)
19860  {
19861  CTEIt++;
19862  continue;
19863  // don't include trains not entered yet when a train is already on the continuation
19864  }
19865  if(!ContinuationEntryVecPosVector.empty())
19866  {
19867  for(unsigned int x = 0; x < ContinuationEntryVecPosVector.size(); x++)
19868  {
19869  if(CTEIt->second.VectorPosition == ContinuationEntryVecPosVector.at(x))
19870  {
19871  LaterTrain = true;
19872  ;
19873  // skip past remaining trains waiting to enter at same point
19874  break;
19875  }
19876  }
19877  }
19878  if(LaterTrain)
19879  {
19880  CTEIt++;
19881  continue;
19882  }
19883  ContinuationEntryVecPosVector.push_back(CTEIt->second.VectorPosition);
19884  AnsiString HeadCode = CTEIt->second.HeadCode;
19885  float CurrentStopTime; // set to 0 at start of function
19886  float LaterStopTime; // set to 0 at start of function
19887  float RecoverableTime; // set to 0 at start of function
19888  int AvTrackSpeed; // set to 0 at start of function
19889  int TrainID = -1; // not yet allocated for train still to enter
19890  int DistanceToExit; //not used for continuation entries
19891  THVShortPair ExitPair;
19892  bool SigControlAndCanPassRedSignal = false; // doesn't apply for a continuation
19893 
19894 //at v2.11.0 found that with *AVPtr set to ...ActionVector.at(0) below instead of ...at(1) to stop signaller control trains throwing an error (because there is no ...at(1) -
19895 //discovered with Birmingham) the LaterStopTime isn't calculated and if a train does something other than depart after an arrival it is still listed in the actions due panel -
19896 //because it just calcs the distance to the red signal and converts that to a time. So here (after v2.11.0) this new test is introduced to determine whether a train is a
19897 //signaller control train (when the ActionVector size is 1) or not (ActionVector size > 1), and the ...at(value) is set accordingly - 0 for signaller control or 1 if not.
19898 
19899  int AtValue = 1;
19900  if(CTEIt->second.TrainDataEntryPtr->ActionVector.size() == 1)
19901  {
19902  AtValue = 0;
19903  }
19904  DistanceToRedSignal = CalcDistanceToRedSignalandStopTime(1, CTEIt->second.VectorPosition, 0,
19905  // EntryPos always 0 for entering at a continuation
19906  SigControlAndCanPassRedSignal, &CTEIt->second.TrainDataEntryPtr->ActionVector.at(AtValue), //see above note
19907  HeadCode, TrainID, CurrentStopTime, LaterStopTime, RecoverableTime, AvTrackSpeed, DistanceToExit, ExitPair);
19908  // for above VectorPosition is the first element to have its length included in the sum, so for a continuation it's the continuation itself
19909  // for a train it's the one in front of LeadElement
19910  if(AvTrackSpeed < 30)
19911  {
19912  AvTrackSpeed = 30;
19913  }
19914  if(DistanceToRedSignal == -1)
19915  {
19916  TimeToAct = 60.0;
19917  }
19918  else
19919  {
19920  int Speed = AvTrackSpeed;
19921  int MaxSpeed = int(CTEIt->second.TrainDataEntryPtr->MaxRunningSpeed);
19922  if(AvTrackSpeed > MaxSpeed)
19923  {
19924  Speed = MaxSpeed;
19925  }
19926  if(CTEIt->second.TrainDataEntryPtr->ActionVector.at(0).SignallerControl) //changed to ...at(0) from at(1) at v2.11.0 as SignallerControl only valid for ..at(0)
19927  // defined in timetable as under signaller control
19928  {
19929  Speed = CTEIt->second.TrainDataEntryPtr->SignallerSpeed;
19930  LaterStopTime = 0;
19931  }
19932  TimeToAct = LaterStopTime + DistanceToRedSignal * 3.6 / 60 / Speed;
19933  // accel & decel taken into account in
19934  // CalcDistanceToRedSignalandStopTime
19935  // 3.6 convertsKm/h to m/s & 60 converts seconds to minutes
19936  // don't need CurrentStopTime or RecoverableTime for continuation entries
19937  float MinsBefEnter = double(CTEIt->first - TTClockTime) * 86400.0 / 60.0;
19938  TimeToAct += MinsBefEnter;
19939  }
19940  THCandTrainPosParam HCandTrainPosParam;
19941  HCandTrainPosParam.first = HeadCode;
19942  HCandTrainPosParam.second = -1 - CTEIt->second.VectorPosition;
19943  // -1-CTE... because 2nd value covers TrainID if +ve &
19944  // continuation track vector position if -ve, -1 allows for vecpos being 0
19945  if(TimeToAct < 59.9) // if 60 don't enter a value in multimap
19946  {
19947  OpTimeToActMultiMapEntry.first = TimeToAct;
19948  OpTimeToActMultiMapEntry.second = HCandTrainPosParam;
19949  OpTimeToActMultiMap.insert(OpTimeToActMultiMapEntry);
19950  }
19951  CTEIt++;
19952  }
19953  }
19954  Utilities->CallLogPop(2081);
19955 }
19956 
19957 // ---------------------------------------------------------------------------
19958 
19960 // new for multiplayer
19961 // clears entries then adds values for running trains
19962 {
19963  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",RebuildTimeToExitMultiMap");
19964  TimeToExitMultiMap.clear();
19965  TTimeToExitMultiMapEntry TimeToExitMultiMapEntry;
19966 
19967  if(!TrainVector.empty())
19968  // build map entries for running trains
19969  {
19970  TExitInfo ExitInfo; //corresponds to TServiceInfo in Interface
19971  THVShortPair ExitPair;
19972  float TimeToExit;
19973  for(unsigned int x = 0; x < TrainVector.size(); x++)
19974  {
19976  ExitInfo.RepeatNumber = short(TrainVectorAt(81, x).RepeatNumber);
19977  ExitInfo.TimeToExitSecs = short(TrainVectorAt(77, x).TimeToExit * 60);
19978  ExitPair = TrainVectorAt(76, x).ExitPair;
19979  if((ExitInfo.TimeToExitSecs >= 3570) || (ExitInfo.TimeToExitSecs < 1)) //59.5 mins or -60 secs
19980  {
19981  ExitInfo.TimeToExitSecs = -1;
19982  }
19983  TimeToExitMultiMapEntry.first = ExitPair;
19984  TimeToExitMultiMapEntry.second = ExitInfo;
19985  TimeToExitMultiMap.insert(TimeToExitMultiMapEntry);
19986  }
19987  }
19988  Utilities->CallLogPop(2323);
19989 }
19990 
19991 // ---------------------------------------------------------------------------
19992 
19993 int TTrainController::CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos,
19994  bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime,
19995  float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
19996 // new v2.2.0
19997 // vectorPosition is the value for the first element to be measured - for a continuation it's the continuation itself, for a train
19998 // it's the one after LeadElement, returns -1 for infinity - i.e. not approaching red signal (e.g. maybe cdt before reach it).
19999 // CurrentStopTime is the time to depart from the current station (if stopped at a station) and LaterStopTime is the total station
20000 // stop times for stations after the current one. DistanceToRedSignal is what the name implies, if -1 is returned the other values
20001 // aren't used - this means there is no display for the train in question
20002 {
20003  Utilities->CallLog.push_back(Utilities->TimeStamp() + "," + AnsiString(Caller) + ",DistanceToRedSignal, " + AnsiString(TrackVectorPosition) + ", " +
20004  AnsiString(TrackVectorPositionEntryPos) + ", " + AVPtr->Command);
20005  int DistanceToRedSignal = 0;
20006  DistanceToExit = -1;
20007  ExitPair.first = -1;
20008  ExitPair.second = -1;
20009  int CumTrackSpeed = 0;
20010  // average track speed, in case need to use in time calc
20011  int TrackSpeedCount = 0;
20012  float KmPerLocationStop;
20013  float MaxAllowableSpeed;
20014 
20015  //below added at v2.6.1
20016  if(TrainID > -1)
20017  {
20018  TTrain &Train = TrainVectorAtIdent(51, TrainID);
20019  Train.DistanceToStationStop = 0; //if find a red signal first then this distance isn't needed
20020  Train.StationStopCalculated = false;
20021  }
20022  AvTrackSpeed = 0;
20023  int CurrentElement = TrackVectorPosition;
20024  int CurrentEntryPos = TrackVectorPositionEntryPos;
20025  int NextElement;
20026  int NextEntryPos;
20027  int NextExitPos;
20028 
20029  CurrentStopTime = 0;
20030  LaterStopTime = 0;
20031  RecoverableTime = 0;
20032  if(CurrentElement == -1) // end element, no action needed
20033  {
20034  Utilities->CallLogPop(2094);
20035  return(-1);
20036  }
20037  int CurrentExitPos;
20038 
20039  // get ExitPos for first element to be measured
20040  if(Track->TrackElementAt(935, CurrentElement).TrackType == Points)
20041  {
20042  if((CurrentEntryPos == 0) || (CurrentEntryPos == 2)) // leading point
20043  {
20044  if(Track->TrackElementAt(936, CurrentElement).Attribute == 0)
20045  {
20046  CurrentExitPos = 1;
20047  }
20048  else
20049  {
20050  CurrentExitPos = 3;
20051  }
20052  }
20053  else
20054  {
20055  CurrentExitPos = 0; // trailing point
20056  }
20057  }
20058  else
20059  {
20060  CurrentExitPos = Track->GetNonPointsOppositeLinkPos(CurrentEntryPos);
20061  }
20062  // get CumTrackSpeed for first measured element
20063 
20064  TConfiguration CurrentExitConfig = Track->TrackElementAt(937, CurrentElement).Config[CurrentExitPos];
20065  int CurrentAttribute = Track->TrackElementAt(938, CurrentElement).Attribute;
20066 
20067  // check if currently stopped at a location, and if so add the remaining dwell time
20068  // can't use CurrentElement as that is in front of LeadElement and might not be at the location
20069  if(TrainID > -1)
20070  // -1 for a continuation and can't be at a location as not yet entered
20071  {
20072  TTrain &Train = TrainVectorAtIdent(39, TrainID); //Train wasn't a reference before v2.6.1 mods so FirstLaterStopRecoverableTime wouldn't be reset for the referenced train
20074  // this used to deduct from RecoverableTime when arrive at a location
20075  if(Train.RevisedStoppedAtLoc())
20076  {
20077  if(Train.StoppedForTrainInFront)
20078  {
20079  Utilities->CallLogPop(2082);
20080  return(-1); // no action needed
20081  }
20082  else if(!((Train.ActionVectorEntryPtr->FormatType == TimeTimeLoc) || (Train.ActionVectorEntryPtr->FormatType == TimeLoc)))
20083  {
20084  Utilities->CallLogPop(2083);
20085  return(-1); // not due a departure so no action needed
20086  }
20087  else // due a departure
20088  {
20089  double TimeToDepart = double(Train.ReleaseTime - TrainController->TTClockTime) * 86400 / 60; // mins to depart
20090  // can't convert a TDateTime to a float directly
20091  CurrentStopTime = float(TimeToDepart);
20092  AVPtr++;
20093  }
20094  }
20095  }
20096  // check if CurrentElement is a red signal, but ok if autosig route after
20097  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20098  // ok if autosig route after red signal
20099  {
20100  int NextElement = Track->TrackElementAt(939, CurrentElement).Conn[CurrentExitPos];
20101  int NextEntryPos = Track->TrackElementAt(940, CurrentElement).ConnLinkPos[CurrentExitPos];
20102  int RouteNumber; // holder for referenced value, not used
20103  if(AllRoutes->GetRouteTypeAndNumber(33, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20104  {
20105  Utilities->CallLogPop(2078);
20106  return(-1);
20107  }
20108  else if(SigControlAndCanPassRedSignal)
20109  // ignore signal and increment CurrentElement to NextElement
20110  {
20111  if(Track->TrackElementAt(941, NextElement).TrackType == Points)
20112  {
20113  if((NextEntryPos == 0) || (NextEntryPos == 2))
20114  // leading entry point
20115  {
20116  if(Track->TrackElementAt(942, NextElement).Attribute == 0)
20117  {
20118  NextExitPos = 1;
20119  }
20120  else
20121  {
20122  NextExitPos = 3;
20123  }
20124  }
20125  else
20126  {
20127  NextExitPos = 0; // trailing entry point
20128  }
20129  }
20130  else
20131  {
20132  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20133  }
20134  CurrentElement = NextElement;
20135  CurrentEntryPos = NextEntryPos;
20136  CurrentExitPos = NextExitPos;
20137  CurrentExitConfig = Track->TrackElementAt(943, CurrentElement).Config[CurrentExitPos];
20138  CurrentAttribute = Track->TrackElementAt(944, CurrentElement).Attribute;
20139  }
20140  else if((TrainID > -1) && (TrainVectorAtIdent(40, TrainID).TrainMode == Timetable)) // ignore signallercontrol or will
20141  // give 'NOW' indication after allowed to pass red signal when LeadMidLag (AllowedToPassRedSignal reset by this point)
20142  {
20143  Utilities->CallLogPop(2084);
20144  return(0);
20145  // stopped with red signal in front, don't need AvSpeedLimit in this case, & if at location awaiting departure dwell time already calculated
20146  }
20147  }
20148  int LaterStopNumber = 0;
20149  int x = 0;
20150  // added in v2.4.0 to prevent endless circling round track loops - spotted by Xeon 09/03/20 & reported by emsil
20151 
20152  while(!((CurrentExitConfig == Signal) && (CurrentAttribute == 0)))
20153  // not red signal next (in fwd direction) so enter loop to calc CumLength
20154  {
20155  x++; // added in v2.4.0 as above
20156  if(x > 5000)
20157  {
20158  Utilities->CallLogPop(2120);
20159  return(-1);
20160  }
20161  if(CurrentEntryPos > 1)
20162  {
20163  DistanceToRedSignal += Track->TrackElementAt(916, CurrentElement).Length23;
20164  CumTrackSpeed += Track->TrackElementAt(945, CurrentElement).SpeedLimit23;
20165  }
20166  else
20167  {
20168  DistanceToRedSignal += Track->TrackElementAt(917, CurrentElement).Length01;
20169  CumTrackSpeed += Track->TrackElementAt(946, CurrentElement).SpeedLimit01;
20170  }
20171  TrackSpeedCount++;
20172 
20173  //added for multiplayer - exiting at a continuation and continuation length already added
20174  if((Track->TrackElementAt(1407, CurrentElement).TrackType == Continuation) && (Track->TrackElementAt(1408, CurrentElement).Config[CurrentExitPos] == End))
20175  {
20176  DistanceToExit = DistanceToRedSignal; //don't need to exit function here as will exit when find that the next Conn value is -1
20177  ExitPair.first = Track->TrackElementAt(1409, CurrentElement).HLoc;
20178  ExitPair.second = Track->TrackElementAt(1410, CurrentElement).VLoc;
20179  //here repeat calcs for MaxAllowableSpeed & AvTrackSpeed as done at end for stop signal
20180  //need here as next element will be -1 so will exit before calcs at end
20181  if(TrackSpeedCount > 0)
20182  {
20183  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20184  }
20185  else // shouldn't reach here but include to prevent divide by zero error
20186  {
20187  if(CurrentEntryPos > 1)
20188  {
20189  MaxAllowableSpeed = Track->TrackElementAt(951, CurrentElement).SpeedLimit23;
20190  }
20191  else
20192  {
20193  MaxAllowableSpeed = Track->TrackElementAt(952, CurrentElement).SpeedLimit01;
20194  }
20195  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20196  }
20197  //calc AvTrackSpeed
20198  if(LaterStopNumber > 0)
20199  {
20200  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20201  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20202  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20203  // average line speed/2 (for half distance accelerating and half decelerating.
20204  }
20205  else
20206  {
20207  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20208  // using linear trendline for accel & decel distance at various speeds
20209  // at half braking, speed never < 60 using this
20210  }
20211  if(AvTrackSpeed > MaxAllowableSpeed)
20212  {
20213  AvTrackSpeed = MaxAllowableSpeed;
20214  }
20215  }
20216 
20217  // added at v2.6.1 to find DistanceToStationStop for trains running early
20218  if(TrainID > -1) //can ignore continuation entries as these don't run early
20219  {
20220  TTrain &Train = TrainVectorAtIdent(52, TrainID);
20221  if(!Train.StationStopCalculated)
20222  {
20223  if(Train.TrainMode == Timetable)
20224  {
20225  bool StopRequired = false;
20226  if(!Train.TimetableFinished && (Train.NameInTimetableBeforeCDT(16, Track->TrackElementAt(1005, CurrentElement).ActiveTrackElementName,
20227  StopRequired) > -1) && ((Track->TrackElementAt(1006, CurrentElement).StationEntryStopLinkPos1 == CurrentEntryPos) ||
20228  (Track->TrackElementAt(1010, CurrentElement).StationEntryStopLinkPos2 == CurrentEntryPos)))
20229  {
20230  // no need to add in the length of element to CumulativeLength
20231  if(StopRequired)
20232  {
20233  Train.DistanceToStationStop = DistanceToRedSignal; // DistanceToRedSignal holds the intermediate distance to this point
20234  Train.StationStopCalculated = true; //don't want to update it with later stops
20235  }
20236  }
20237  }
20238  }
20239  }
20240  // check for train in front, but if on a bridge on other track then ok
20241  TTrackElement TE = Track->TrackElementAt(947, CurrentElement);
20242  int TrainOnElement;
20243  if(TE.TrackType != Bridge)
20244  {
20245  TrainOnElement = TE.TrainIDOnElement;
20246  }
20247  else
20248  {
20249  if(CurrentEntryPos > 1)
20250  {
20251  TrainOnElement = TE.TrainIDOnBridgeTrackPos23;
20252  }
20253  else
20254  {
20255  TrainOnElement = TE.TrainIDOnBridgeTrackPos01;
20256  }
20257  }
20258  if((TrainOnElement > -1) && (TrainOnElement != TrainID))
20259  // train in front before red signal
20260  {
20261  Utilities->CallLogPop(2085);
20262  return(-1);
20263  }
20264  // add to stoptime if required
20265  if(Track->TrackElementAt(948, CurrentElement).ActiveTrackElementName != "")
20266  {
20267  double StopTimeDouble;
20268  while(AVPtr->FormatType == PassTime)
20269  {
20270  AVPtr++; // skip past any passes
20271  }
20272  if((Track->TrackElementAt(949, CurrentElement).ActiveTrackElementName == AVPtr->LocationName) && ((AVPtr->FormatType == TimeLoc) ||
20273  (AVPtr->FormatType == TimeTimeLoc)))
20274  // stop due here so calc dwell time & advance Ptr
20275  {
20276  if(AVPtr->FormatType == TimeTimeLoc)
20277  {
20278  LaterStopNumber++;
20279  StopTimeDouble = double(AVPtr->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20280  if(StopTimeDouble < 0.5)
20281  {
20282  StopTimeDouble = 0.5;
20283  }
20284  // at least 30 secs delay at station
20285  // can't convert a TDateTime to a float directly
20286  LaterStopTime += float(StopTimeDouble);
20287  RecoverableTime += StopTimeDouble - 0.5;
20288  if((LaterStopNumber == 1) && (TrainID > -1))
20289  {
20290  TrainVectorAtIdent(41, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20291  }
20292  AVPtr++;
20293  }
20294  else if((AVPtr->FormatType == TimeLoc) && (AVPtr->ArrivalTime != TDateTime(-1))) // must be an arrival
20295  {
20296  if((AVPtr + 1)->FormatType == TimeLoc)
20297  // must be a departure
20298  {
20299  LaterStopNumber++;
20300  StopTimeDouble = double((AVPtr + 1)->DepartureTime - AVPtr->ArrivalTime) * 86400.0 / 60.0;
20301  // can't convert a TDateTime to a float directly
20302  if(StopTimeDouble < 0.5)
20303  {
20304  StopTimeDouble = 0.5;
20305  }
20306  // at least 30 secs delay at station
20307  LaterStopTime += float(StopTimeDouble);
20308  RecoverableTime += StopTimeDouble - 0.5;
20309  if((LaterStopNumber == 1) && (TrainID > -1))
20310  {
20311  TrainVectorAtIdent(42, TrainID).FirstLaterStopRecoverableTime = RecoverableTime;
20312  }
20313  AVPtr++;
20314  AVPtr++;
20315  }
20316  else // not a departure, does something else at the location so no calculation needed
20317  {
20318  Utilities->CallLogPop(2086);
20319  return(-1);
20320  }
20321  }
20322  }
20323  }
20324  NextElement = Track->TrackElementAt(950, CurrentElement).Conn[CurrentExitPos];
20325  if(NextElement == -1) // reached end element, no action needed
20326  {
20327  Utilities->CallLogPop(2077);
20328  return(-1);
20329  }
20330  NextEntryPos = Track->TrackElementAt(919, CurrentElement).ConnLinkPos[CurrentExitPos];
20331  // get NextExitPos
20332  if(Track->TrackElementAt(920, NextElement).TrackType == Points)
20333  {
20334  if((NextEntryPos == 0) || (NextEntryPos == 2))
20335  // leading entry point
20336  {
20337  if(Track->TrackElementAt(921, NextElement).Attribute == 0)
20338  {
20339  NextExitPos = 1;
20340  }
20341  else
20342  {
20343  NextExitPos = 3;
20344  }
20345  }
20346  else
20347  {
20348  NextExitPos = 0; // trailing entry point
20349  }
20350  }
20351  else
20352  {
20353  NextExitPos = Track->GetNonPointsOppositeLinkPos(NextEntryPos);
20354  }
20355  CurrentElement = NextElement;
20356  CurrentEntryPos = NextEntryPos;
20357  CurrentExitPos = NextExitPos;
20358  CurrentExitConfig = Track->TrackElementAt(922, CurrentElement).Config[CurrentExitPos];
20359  CurrentAttribute = Track->TrackElementAt(923, CurrentElement).Attribute;
20360  }
20361  if((CurrentExitConfig == Signal) && (CurrentAttribute == 0))
20362  // ok if autosig route after red signal, no action needed
20363  {
20364  int NextElement = Track->TrackElementAt(924, CurrentElement).Conn[CurrentExitPos];
20365  int NextEntryPos = Track->TrackElementAt(925, CurrentElement).ConnLinkPos[CurrentExitPos];
20366  int RouteNumber; // holder for referenced value, not used
20367  if(AllRoutes->GetRouteTypeAndNumber(31, NextElement, NextEntryPos, RouteNumber) == TAllRoutes::AutoSigsRoute)
20368  {
20369  Utilities->CallLogPop(2095);
20370  return(-1);
20371  }
20372  }
20373 
20374  if(TrackSpeedCount > 0)
20375  {
20376  MaxAllowableSpeed = CumTrackSpeed / TrackSpeedCount;
20377  }
20378  else // shouldn't reach here but include to prevent divide by zero error
20379  {
20380  if(CurrentEntryPos > 1)
20381  {
20382  MaxAllowableSpeed = Track->TrackElementAt(1433, CurrentElement).SpeedLimit23;
20383  }
20384  else
20385  {
20386  MaxAllowableSpeed = Track->TrackElementAt(1434, CurrentElement).SpeedLimit01;
20387  }
20388  // Train MaxRunningSpeed taken into account in RebuildOpTimeToActMultimap
20389  }
20390 
20391  if(LaterStopNumber > 0)
20392  {
20393  KmPerLocationStop = float(DistanceToRedSignal) / LaterStopNumber / 1000; // m to km
20394  AvTrackSpeed = (8.75 * KmPerLocationStop) + 44;
20395  }
20396  else
20397  // Av speed calculation based on formula: Speed = 8.75*(kms/location stop) + 44 km/sec (from experiments), subject to a maximum of
20398  // average line speed/2 (for half distance accelerating and half decelerating.
20399  {
20400  AvTrackSpeed = (sqrt(float(DistanceToRedSignal) / 1000) * 44) + 60;
20401  // using linear trendline for accel & decel distance at various speeds
20402  // at half braking, speed never < 60 using this
20403  }
20404  if(AvTrackSpeed > MaxAllowableSpeed)
20405  {
20406  AvTrackSpeed = MaxAllowableSpeed;
20407  }
20408  Utilities->CallLogPop(2096);
20409  return(DistanceToRedSignal);
20410 }
20411 
20412 // ---------------------------------------------------------------------------
20413 // end of TTrainController entries
20414 // ---------------------------------------------------------------------------
TTrain::LinkOccupied
bool LinkOccupied(int Caller, int TrackVectorPosition, int LinkNumber)
Added at v1.2.0: true if any part of train on specific link, false otherwise, including no link prese...
Definition: TrainUnit.cpp:8548
TAllRoutes::TrackIsInARoute
bool TrackIsInARoute(int Caller, int TrackVectorPosition, int LinkPos)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17648
TActionVectorEntry::EventTime
TDateTime EventTime
Definition: TrainUnit.h:136
TTrain::AllowedToPassRedSignal
bool AllowedToPassRedSignal
set when train has been called on, or when under signaller control and instructed to pass a red signa...
Definition: TrainUnit.h:379
JoinedByOther
@ JoinedByOther
Definition: TrainUnit.h:52
TTrainController::CheckShuttleRepeatTime
bool CheckShuttleRepeatTime(int Caller, TDateTime ForwardEventTime, TDateTime ReverseEventTime, int RepeatMinutes)
Check that shuttle link services have consistent times, true for success.
Definition: TrainUnit.cpp:14588
TTrain::ZeroPowerNoNewShuttleFromNonRepeatMessage
bool ZeroPowerNoNewShuttleFromNonRepeatMessage
Definition: TrainUnit.h:345
TTrain::ZeroPowerNoNewServiceMessage
bool ZeroPowerNoNewServiceMessage
Definition: TrainUnit.h:344
TTrain::VOffset
int VOffset[4]
each headcode character is an 8x8 pixel graphic and must be placed within a 16x16 pixel element,...
Definition: TrainUnit.h:481
TTrainController
Handles all train and timetable activities, only one object created.
Definition: TrainUnit.h:693
TTrain::TTrain
TTrain(int Caller, int RearStartElementIn, int RearStartExitPosIn, AnsiString InputCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, TTrainMode TrainMode, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerMaxSpeed)
Constructor, sets listed member values.
Definition: TrainUnit.cpp:71
TAllRoutes::LockedRouteVector
TLockedRouteVector LockedRouteVector
the vector that stores all the locked routes on the railway
Definition: TrackUnit.h:1690
TRailGraphics::smOrange
Graphics::TBitmap * smOrange
Definition: GraphicUnit.h:899
TActionVector
std::vector< TActionVectorEntry > TActionVector
contains all actions for a single train
Definition: TrainUnit.h:174
TUtilities::LoadFileString
AnsiString LoadFileString(std::ifstream &InFile)
loads a string value from the file
Definition: Utilities.cpp:190
TTrainController::CheckNonRepeatingShuttleLinksAndSetData
bool CheckNonRepeatingShuttleLinksAndSetData(int Caller, AnsiString MainHeadCode, AnsiString NonRepeatingHeadCode, bool GiveMessages)
A timetable validation function where cross references are checked for validity for non-repeating shu...
Definition: TrainUnit.cpp:14611
TTrainController::CheckStartPositionValidity
bool CheckStartPositionValidity(int Caller, AnsiString RearElementStr, AnsiString FrontElementStr, bool GiveMessages)
A timetable validation function where train starting positions are checked for validity,...
Definition: TrainUnit.cpp:14256
TTrain::ChangeTrainDirection
void ChangeTrainDirection(int Caller, bool NoLogFlag)
Reverses the direction of motion of the train.
Definition: TrainUnit.cpp:6245
TTrain::MidEntryPos
int MidEntryPos
Definition: TrainUnit.h:366
TTrainController::RebuildOpTimeToActMultimap
void RebuildOpTimeToActMultimap(int Caller)
new v2.2.0 for OperatorActionPanel
Definition: TrainUnit.cpp:19788
TTrainController::PwrHigh
bool PwrHigh
Definition: TrainUnit.h:801
TTrainController::BuildContinuationTrainExpectationMultiMap
void BuildContinuationTrainExpectationMultiMap(int Caller)
populate the ContinuationTrainExpectationMultiMap during timetable loading
Definition: TrainUnit.cpp:15653
TFixedTrackPiece::GraphicPtr
Graphics::TBitmap * GraphicPtr
the track bitmap for display on the zoomed-in railway
Definition: TrackUnit.h:91
SignallerMoveForwards
@ SignallerMoveForwards
Definition: TrainUnit.h:53
TRailGraphics::CodeR
Graphics::TBitmap * CodeR
Definition: GraphicUnit.h:995
Create
@ Create
Definition: TrainUnit.h:52
TTrainController::TContinuationEntryVecPosVector
std::vector< int > TContinuationEntryVecPosVector
ensures only one train displayed for a given continuation
Definition: TrainUnit.h:780
TAllRoutes::SetAllRearwardsSignals
void SetAllRearwardsSignals(int Caller, int Attribute, int RouteNumber, int RouteStartPosition)
Set rearwards signals from the specified route starting position.
Definition: TrackUnit.cpp:18654
Arrive
@ Arrive
Definition: TrainUnit.h:52
ChangeDirection
@ ChangeDirection
Definition: TrainUnit.h:52
TTrain::ZeroPowerNoCDTMessage
bool ZeroPowerNoCDTMessage
Definition: TrainUnit.h:343
TTrain::CallingOnFlag
bool CallingOnFlag
calling on permitted
Definition: TrainUnit.h:385
Depart
@ Depart
Definition: TrainUnit.h:52
TRailGraphics::gl89set
Graphics::TBitmap * gl89set
Definition: GraphicUnit.h:716
TTrainController::CheckHeadCodeValidity
bool CheckHeadCodeValidity(int Caller, bool GiveMessages, AnsiString HeadCode)
Returns true if the headcode complies with requirements.
Definition: TrainUnit.cpp:11459
TRailGraphics::gl88set
Graphics::TBitmap * gl88set
Definition: GraphicUnit.h:714
clBufferStopBackground
#define clBufferStopBackground
Definition: GraphicUnit.h:291
TTrain::MaxRunningSpeed
double MaxRunningSpeed
the current maximum train running speed
Definition: TrainUnit.h:427
TTrack::BarriersDownVector
TActiveLCVector BarriersDownVector
vector of LCs with barriers down
Definition: TrackUnit.h:780
TPrefDirElement::GetXLinkPos
int GetXLinkPos() const
Returns the XLink array position.
Definition: TrackUnit.h:281
TTrack::IsLCBarrierDownAtHV
bool IsLCBarrierDownAtHV(int Caller, int HLoc, int VLoc)
True if an open (to trains) level crossing is found at H & V.
Definition: TrackUnit.cpp:7235
TAllRoutes::TLockedRouteClass::TruncateTrackVectorPosition
unsigned int TruncateTrackVectorPosition
the TrackVector position of the element selected for truncation
Definition: TrackUnit.h:1607
RestoreTimetableControl
@ RestoreTimetableControl
Definition: TrainUnit.h:53
FailCrashed
@ FailCrashed
Definition: TrainUnit.h:41
TRailGraphics::TempHeadCode
Graphics::TBitmap * TempHeadCode
Definition: GraphicUnit.h:905
TAllRoutes::AutoSigsRoute
@ AutoSigsRoute
Definition: TrackUnit.h:1619
TTrack::TSigElement::Attribute
int Attribute
the signal state - red, yellow, double yellow or green
Definition: TrackUnit.h:711
TActionVectorEntry::LocationType
TTimetableLocationType LocationType
indicates where the train is when the relevant action occurs
Definition: TrainUnit.h:142
TTrack::GapFlashGreenPosition
int GapFlashGreenPosition
Definition: TrackUnit.h:764
TAllRoutes::TRouteElementPair
std::pair< int, unsigned int > TRouteElementPair
defines a specific element in a route, the first (int) value is the vector position in the AllRoutesV...
Definition: TrackUnit.h:1631
NamedNonStationLocation
@ NamedNonStationLocation
Definition: TrackUnit.h:66
FinRemHere
@ FinRemHere
Definition: TrainUnit.h:66
TTrain::HeadCodeGrPtr
Graphics::TBitmap * HeadCodeGrPtr[4]
points to the headcode segment graphics e.g. 5,A,4,7.
Definition: TrainUnit.h:498
TTrainController::TContinuationTrainExpectationEntry::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the service entry in the timetable's TrainDataVector
Definition: TrainUnit.h:741
FSHNewService
@ FSHNewService
Definition: TrainUnit.h:67
TTrain::MaxBrakeRate
double MaxBrakeRate
the maximum brake rate that the train can achieve
Definition: TrainUnit.h:431
TTimetableLocationType
TTimetableLocationType
Definition: TrainUnit.h:71
TAllRoutes::DiagonalFouledByRoute
bool DiagonalFouledByRoute(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
As above but only checks for a route (may or may not be a train present (new at v1....
Definition: TrackUnit.cpp:19403
TTrainController::CheckForDuplicateCrossReferences
bool CheckForDuplicateCrossReferences(int Caller, AnsiString MainHeadCode, AnsiString SecondHeadCode, bool GiveMessages)
A timetable validation function where referenced services are checked for uniqueness,...
Definition: TrainUnit.cpp:13534
TTrackElement::StationEntryStopLinkPos2
int StationEntryStopLinkPos2
Used for track at platforms and non-station named locations to mark the train front element stop posi...
Definition: TrackUnit.h:150
TTrain::MidExitPos
int MidExitPos
Definition: TrainUnit.h:366
TRailGraphics::CodeD
Graphics::TBitmap * CodeD
Definition: GraphicUnit.h:981
TTrain::TrainFailed
bool TrainFailed
Definition: TrainUnit.h:411
TUtilities::CheckFileStringZeroDelimiter
bool CheckFileStringZeroDelimiter(std::ifstream &InFile)
checks that the value is a string ('0' only accepted as the delimiter), returns true for success
Definition: Utilities.cpp:435
DisplayUnit.h
TRailGraphics::Code_w
Graphics::TBitmap * Code_w
Definition: GraphicUnit.h:964
FailDerailed
@ FailDerailed
Definition: TrainUnit.h:41
TTrainController::SPADWarning
bool SPADWarning
Definition: TrainUnit.h:787
TTrain::ZeroPowerNoFrontSplitMessage
bool ZeroPowerNoFrontSplitMessage
Definition: TrainUnit.h:339
TRailGraphics::Code_s
Graphics::TBitmap * Code_s
Definition: GraphicUnit.h:960
TTrainController::GetExitLocationAndAt
AnsiString GetExitLocationAndAt(int Caller, TNumList &ExitList, AnsiString &AllowedExits) const
Check all timetable names in ExitList, if all same return " at [name]" + AllowableExits = elements,...
Definition: TrainUnit.cpp:19099
TTrain::SaveOneSessionTrain
void SaveOneSessionTrain(int Caller, std::ofstream &OutFile)
Data for a single train is saved to a session file.
Definition: TrainUnit.cpp:7424
TTrain::DepartureTimeSet
bool DepartureTimeSet
set when stopped at a location and the next action is departure (set in UpdateTrain when ReleaseTime ...
Definition: TrainUnit.h:387
TTrain::Plotted
bool Plotted
set when train plotted on screen
Definition: TrainUnit.h:472
TTrain::LagElement
int LagElement
Definition: TrainUnit.h:366
TTrain::RemainHere
void RemainHere(int Caller)
Sends the 'train terminated' message to the performance log and sets TimetableFinished to true.
Definition: TrainUnit.cpp:6366
TTrainController::BufferAttentionWarning
bool BufferAttentionWarning
Definition: TrainUnit.h:787
TTrainController::TrainVectorAtIdent
TTrain & TrainVectorAtIdent(int Caller, int TrainID)
Return a reference to the train with ID TrainID, carries out validity checking on TrainID.
Definition: TrainUnit.cpp:9824
TPrefDirElement::GetRouteEXGraphicPtr
Graphics::TBitmap * GetRouteEXGraphicPtr()
Returns route graphic.
Definition: TrackUnit.h:317
THVShortPair
std::pair< short, short > THVShortPair
Definition: InterfaceUnit.h:81
TRailGraphics::smLightBlue
Graphics::TBitmap * smLightBlue
Definition: GraphicUnit.h:896
TTrainController::TContinuationAutoSigEntry::RouteNumber
int RouteNumber
the AllRoutesVector position of the route that the continuation is in
Definition: TrainUnit.h:715
TTrainController::StopTTClockMessage
void StopTTClockMessage(int Caller, AnsiString Message)
sends a message to the user and stops the timetable clock while it is displayed
Definition: TrainUnit.cpp:15384
TExitInfo::TimeToExitSecs
short TimeToExitSecs
Definition: TrainUnit.h:110
TTrainController::OperatingTrainLateArr
int OperatingTrainLateArr
< all these set to 0 in constructor
Definition: TrainUnit.h:846
TRailGraphics::CodeJ
Graphics::TBitmap * CodeJ
Definition: GraphicUnit.h:987
TUtilities::IncrementAnsiTimeOneMinute
AnsiString IncrementAnsiTimeOneMinute(AnsiString TimeVal)
takes "HH:MM" and increments it to "HH:MX", where MX == MM + 1, incrementing the hour if necessary
Definition: Utilities.cpp:826
TRailGraphics::CodeQ
Graphics::TBitmap * CodeQ
Definition: GraphicUnit.h:994
TDisplay::GetOutputLog9
TLabel * GetOutputLog9()
Definition: DisplayUnit.h:185
TAllRoutes::RemoveRouteElement
void RemoveRouteElement(int Caller, int HLoc, int VLoc, int ELink)
Erases the route element from Route2MultiMap and from the PrefDirVector.
Definition: TrackUnit.cpp:18444
TTrainController::CrashWarning
bool CrashWarning
Definition: TrainUnit.h:787
TTrainController::ControllerGetNewServiceDepartureInfo
AnsiString ControllerGetNewServiceDepartureInfo(int Caller, TActionVectorIterator Ptr, int RptNum, TTrainDataEntry *TDEPtr, TTrainDataEntry *LinkedTrainDataPtr, int IncrementalMinutes, AnsiString RetStr)
Similar to TTrain::GetNewServiceDepartureInfo for use in ContinuationEntryFloatingTTString.
Definition: TrainUnit.cpp:10015
TTrainController::CheckLocationValidity
bool CheckLocationValidity(int Caller, AnsiString LocStr, bool GiveMessages, bool CheckLocationsExistInRailway)
Returns true if the location name complies with requirements.
Definition: TrainUnit.cpp:11412
TAllRoutes::TLockedRouteClass::LastXLinkPos
int LastXLinkPos
the XLinkPos value of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1611
TTrain::PlotTrain
void PlotTrain(int Caller, TDisplay *Disp)
Plots the train on the display in normal (zoomed-in) mode.
Definition: TrainUnit.cpp:8520
TNumListIterator
TNumList::iterator TNumListIterator
Definition: TrainUnit.h:97
FailLevelCrossingCrash
@ FailLevelCrossingCrash
Definition: TrainUnit.h:43
TActionVectorEntry::DepartureTime
TDateTime DepartureTime
relevant times at which the action is timetabled, zeroed on creation so change to -1 as a marker for ...
Definition: TrainUnit.h:136
TRailGraphics::Code_f
Graphics::TBitmap * Code_f
Definition: GraphicUnit.h:947
FailCreateLockedRoute
@ FailCreateLockedRoute
Definition: TrainUnit.h:42
TTrainController::TContinuationTrainExpectationEntry::IncrementalDigits
int IncrementalDigits
Repeat headcode separation.
Definition: TrainUnit.h:737
TTrainController::SaveSessionTrains
void SaveSessionTrains(int Caller, std::ofstream &SessionFile)
save trains to a session file
Definition: TrainUnit.cpp:15399
TOneCompleteFormattedTrain::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:265
Intermediate
@ Intermediate
Definition: TrainUnit.h:77
TRailGraphics::Code_d
Graphics::TBitmap * Code_d
Definition: GraphicUnit.h:945
TTrain::JoinedBy
void JoinedBy(int Caller)
Carry out the actions needed when a train is waiting to be joined by another train.
Definition: TrainUnit.cpp:6174
FailIncorrectExit
@ FailIncorrectExit
Definition: TrainUnit.h:43
TRailGraphics::Code_t
Graphics::TBitmap * Code_t
Definition: GraphicUnit.h:961
TRailGraphics::ChangeBackgroundColour
void ChangeBackgroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour, bool &ColourError)
Definition: GraphicUnit.cpp:3499
TTrain::GetLeadElement
int GetLeadElement()
get LeadElement - used in RouteLockingRequired in TrackUnit.cpp
Definition: TrainUnit.h:675
TimeCmd
@ TimeCmd
Definition: TrainUnit.h:66
TTrain::BackgroundPtr
Graphics::TBitmap * BackgroundPtr[4]
the existing track graphic that the train headcode segment covers up (one for each headcode segment)
Definition: TrainUnit.h:494
TUtilities::LoadFileDouble
double LoadFileDouble(std::ifstream &InFile)
loads a double value from the file (converts from a string to a double) and uses the local decimal po...
Definition: Utilities.cpp:172
TTrain::DistanceToStationStop
int DistanceToStationStop
calculated in UpdateTrain & used in CalcDistanceToRedSignalandStopTime to cater for trains running ea...
Definition: TrainUnit.h:454
TTrainController::EntryPos
int EntryPos(int Caller, int TrainIDIn, int TrackVectorNumber)
Return the track entry link (Link[]) array position for the given train on track element at track vec...
Definition: TrainUnit.cpp:9786
SignallerLeave
@ SignallerLeave
Definition: TrainUnit.h:54
TTrain::PlotEntryPos
int PlotEntryPos[4]
the LinkPos value corresponding to the train entry link of the element where each of the 4 headcode c...
Definition: TrainUnit.h:487
NotStarted
@ NotStarted
Definition: TrainUnit.h:88
TTrack::OneNamedLocationElementAtLocation
bool OneNamedLocationElementAtLocation(int Caller, AnsiString LocationName)
True if there is at least one named location element with name 'LocationName', used in timetable inte...
Definition: TrackUnit.cpp:10803
TTrainController::SPADEvents
int SPADEvents
Definition: TrainUnit.h:840
TTrainController::LastTTTime
AnsiString LastTTTime
Stores the last time used in the timetable as an AnsiString - used for timetable analysis.
Definition: TrainUnit.h:783
TTrain::ZeroPowerNoRepeatShuttleMessage
bool ZeroPowerNoRepeatShuttleMessage
Definition: TrainUnit.h:346
TTrackElement::SigAspect
enum TTrackElement::@1 SigAspect
TRailGraphics::gl90set
Graphics::TBitmap * gl90set
Definition: GraphicUnit.h:719
TTrain::ActionVectorEntryPtr
TActionVectorEntry * ActionVectorEntryPtr
points to the current position in the ActionVector (a member of the TTrainDataEntry class)
Definition: TrainUnit.h:373
TTrainDataEntry
Contains all data for a single timetable service entry.
Definition: TrainUnit.h:207
LeadMid
@ LeadMid
Definition: TrainUnit.h:298
FailMissedPass
@ FailMissedPass
Definition: TrainUnit.h:42
clSignalStopBackground
#define clSignalStopBackground
Definition: GraphicUnit.h:299
TTrainController::LateDeps
int LateDeps
Definition: TrainUnit.h:830
TAllRoutes::IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber
bool IsElementInLockedRouteGetPrefDirElementGetLockedVectorNumber(int Caller, int TrackVectorPosition, int XLinkPos, TPrefDirElement &PrefDirElement, int &LockedVectorNumber)
Checks whether the preferred direction element at TrackVectorPosition with XLinkPos value is in a loc...
Definition: TrackUnit.cpp:18918
TrackUnit.h
TTrainController::CheckStartAllowable
bool CheckStartAllowable(int Caller, int RearPosition, int RearExitPos, AnsiString HeadCode, bool ReportFlag, TActionEventType &EventType)
Called when trying to introduce a new train - checks for points in correct orientation,...
Definition: TrainUnit.cpp:14336
TTrainController::RebuildTimeToExitMultiMap
void RebuildTimeToExitMultiMap(int Caller)
new for multiplayer
Definition: TrainUnit.cpp:19959
TRailGraphics::Code_r
Graphics::TBitmap * Code_r
Definition: GraphicUnit.h:959
TTrain::SendMissedActionLogs
void SendMissedActionLogs(int Caller, int IncNum, TActionVectorEntry *Ptr)
Missed actions (see NameInTimetableBeforeCDT above) sent to the performance log and performance file.
Definition: TrainUnit.cpp:6387
TTrainController::TContinuationTrainExpectationEntry::RepeatNumber
int RepeatNumber
service RepeatNumber
Definition: TrainUnit.h:733
TRailGraphics::CodeX
Graphics::TBitmap * CodeX
Definition: GraphicUnit.h:1001
TOneTrainFormattedEntry::Time
AnsiString Time
the time of the action as a string
Definition: TrainUnit.h:252
TRailGraphics::CodeT
Graphics::TBitmap * CodeT
Definition: GraphicUnit.h:997
TTrainDataEntry::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:215
TTrainController::LastTrainLoaded
int LastTrainLoaded
displays last train loaded from session file, used for debugging
Definition: TrainUnit.h:850
TTrainController::NotStartedTrainLateMins
float NotStartedTrainLateMins
total late minutes of trains that haven't started yet on exit operation for locations not reached yet
Definition: TrainUnit.h:807
Utilities.h
TTrainController::TContinuationAutoSigVectorIterator
TContinuationAutoSigVector::iterator TContinuationAutoSigVectorIterator
Definition: TrainUnit.h:723
TTrain::SkippedDeparture
bool SkippedDeparture
< used for terminating a service early and becoming new follow-on service
Definition: TrainUnit.h:327
TTrain::TrainOnContinuation
bool TrainOnContinuation(int Caller)
Returns true if any part of train on a continuation - called when checking for failures,...
Definition: TrainUnit.cpp:8991
TTrainController::CheckSessionLockedRoutes
bool CheckSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for locked routes, true for success.
Definition: TrainUnit.cpp:15502
FailTrainEntry
@ FailTrainEntry
Definition: TrainUnit.h:40
MidLag
@ MidLag
Definition: TrainUnit.h:298
TDisplay::Update
void Update()
Repaint the screen display.
Definition: DisplayUnit.h:222
TTrainController::LocServiceTimesDepTimeSort
bool LocServiceTimesDepTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:887
TTrainController::CheckSessionTrains
bool CheckSessionTrains(int Caller, std::ifstream &InFile)
Part of the session file integrity check for train entries, true for success.
Definition: TrainUnit.cpp:15440
StartNew
@ StartNew
Definition: TrainUnit.h:66
TakeSignallerControl
@ TakeSignallerControl
Definition: TrainUnit.h:52
LeadMidLag
@ LeadMidLag
Definition: TrainUnit.h:298
TTrack::GapFlashGreen
TGraphicElement * GapFlashGreen
Definition: TrackUnit.h:784
TTrain::ZeroPowerNoRepeatShuttleOrNewServiceMessage
bool ZeroPowerNoRepeatShuttleOrNewServiceMessage
flags to indicate whether the respective message has been sent
Definition: TrainUnit.h:347
TrainFailure
@ TrainFailure
Definition: TrainUnit.h:53
TTrain::MinsDelayed
float MinsDelayed
new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain
Definition: TrainUnit.h:442
TTrack::TSigElement::SigPtr
Graphics::TBitmap * SigPtr
pointer to the graphic
Definition: TrackUnit.h:713
TTrainController::TimeToExitMultiMap
TTimeToExitMultiMap TimeToExitMultiMap
Map of times to exit & exit coordinates.
Definition: TrainUnit.h:990
TAllRoutes::FindRouteNumberFromRoute2MultiMapNoErrors
bool FindRouteNumberFromRoute2MultiMapNoErrors(int Caller, int HLoc, int VLoc, int ELink, int &RouteNumber)
If a route is present at H, V & Elink returns true with RouteNumber giving vector position in AllRout...
Definition: TrackUnit.cpp:18192
TTrainController::Last2CharactersBothDigits
bool Last2CharactersBothDigits(int Caller, AnsiString HeadCode)
Checks the last two characters in HeadCode and returns true if both are digits.
Definition: TrainUnit.cpp:11020
TRailGraphics::Code2
Graphics::TBitmap * Code2
Definition: GraphicUnit.h:970
TTrack::GetVectorPositionsFromInactiveTrackMap
TIMPair GetVectorPositionsFromInactiveTrackMap(int Caller, int HLoc, int VLoc, bool &FoundFlag)
Similar to GetVectorPositionFromTrackMap but for inactive elements, a pair is returned because there ...
Definition: TrackUnit.cpp:5827
TTrain::BackgroundColour
TColor BackgroundColour
the background colour of the train's headcode graphics
Definition: TrainUnit.h:501
TTrainOperatingData::EventReported
TActionEventType EventReported
Definition: TrainUnit.h:186
TTrain
Definition: TrainUnit.h:304
TRailGraphics::Code_z
Graphics::TBitmap * Code_z
Definition: GraphicUnit.h:967
TTrainController::CheckTimeValidity
bool CheckTimeValidity(int Caller, AnsiString TimeStr, TDateTime &Time)
returns true if the time complies with requirements
Definition: TrainUnit.cpp:11039
clSignallerStopped
#define clSignallerStopped
Definition: GraphicUnit.h:298
TTrackElement::TrainIDOnBridgeTrackPos23
int TrainIDOnBridgeTrackPos23
Set to the TrainID value when a train is present on the element, bridges can have two trains present ...
Definition: TrackUnit.h:152
TOnePrefDir::GetFixedPrefDirElementAt
const TPrefDirElement & GetFixedPrefDirElementAt(int Caller, int At) const
Return a non-modifiable element at PrefDirVector position 'At'.
Definition: TrackUnit.cpp:11433
FailBuffersPreventingStart
@ FailBuffersPreventingStart
Definition: TrainUnit.h:43
TTrainController::LocServiceTimesArrTimeSort
bool LocServiceTimesArrTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:882
TOneTrainFormattedEntry::Action
AnsiString Action
includes location if relevant
Definition: TrainUnit.h:250
TTrainController::ContinuationTrainExpectationMultiMap
TContinuationTrainExpectationMultiMap ContinuationTrainExpectationMultiMap
Multimap for TContinuationTrainExpectationEntry objects, the access key is the expectation time.
Definition: TrainUnit.h:864
TTrain::StoppedForTrainInFront
bool StoppedForTrainInFront
Definition: TrainUnit.h:478
GapJump
@ GapJump
Definition: TrackUnit.h:65
TRailGraphics::CodeK
Graphics::TBitmap * CodeK
Definition: GraphicUnit.h:988
TTrainController::MissedStops
int MissedStops
Definition: TrainUnit.h:833
NoSequence
@ NoSequence
Definition: TrainUnit.h:77
clCallOnBackground
#define clCallOnBackground
Definition: GraphicUnit.h:292
TTrackElement::Length01
int Length01
Definition: TrackUnit.h:148
TTrackElement::SpeedLimit01
int SpeedLimit01
Definition: TrackUnit.h:148
TTrain::TerminatedMessageSent
bool TerminatedMessageSent
set when a 'train terminated' message has been logged, to prevent its being logged more than once
Definition: TrainUnit.h:407
Finish
@ Finish
Definition: TrainUnit.h:77
TTrain::FinishJoin
void FinishJoin(int Caller)
Carry out the actions needed when a train is waiting to join another train.
Definition: TrainUnit.cpp:6125
TTrainController::FinishedOperation
void FinishedOperation(int Caller)
called when exiting operation mode to delete all trains and timetable data etc
Definition: TrainUnit.cpp:9417
TTrain::MidElement
int MidElement
Definition: TrainUnit.h:366
TAllRoutes::CallonVector
std::vector< TCallonEntry > CallonVector
the store of all call-on entries
Definition: TrackUnit.h:1658
TTrain::LeadElement
int LeadElement
Definition: TrainUnit.h:366
clDerailedBackground
#define clDerailedBackground
Definition: GraphicUnit.h:294
TTrain::RepeatNumber
int RepeatNumber
indicates which of the repeating services this train represents (0 = first service)
Definition: TrainUnit.h:360
TTrainDataEntry::ActionVector
TActionVector ActionVector
all the actions for the train
Definition: TrainUnit.h:225
TTrainController::BaseTime
TDateTime BaseTime
CurrentDateTime (i.e. real time) when operation restarts after a pause.
Definition: TrainUnit.h:696
TTrain::StartSpeed
int StartSpeed
the speed of the train when introduced into the railway (in km/h)
Definition: TrainUnit.h:364
TExitInfo::TExitInfo
TExitInfo()
Definition: TrainUnit.cpp:62
TTrainController::LoadSessionLockedRoutes
void LoadSessionLockedRoutes(int Caller, std::ifstream &SessionFile)
load locked routes from a session file
Definition: TrainUnit.cpp:15481
TTrainController::EarlyPasses
int EarlyPasses
Definition: TrainUnit.h:826
TTrain::HeadCode
AnsiString HeadCode
needs own HeadCode because repeat entries will differ from TrainDataEntry.HeadCode
Definition: TrainUnit.h:323
TDisplay::GetOutputLog7
TLabel * GetOutputLog7()
Definition: DisplayUnit.h:175
TTrainController::TOpTimeToActMultiMapEntry
std::pair< float, THCandTrainPosParam > TOpTimeToActMultiMapEntry
Definition: TrainUnit.h:778
FailMissedSplit
@ FailMissedSplit
Definition: TrainUnit.h:41
TTrain::EntryTime
TDateTime EntryTime
Definition: TrainUnit.h:458
TExitInfo::RepeatNumber
short RepeatNumber
Definition: TrainUnit.h:109
TTrainController::TContinuationAutoSigEntry::FirstDelay
double FirstDelay
Definition: TrainUnit.h:711
TTrainController::SaveTrainDataVectorToFile
void SaveTrainDataVectorToFile(int Caller)
diagnostic function to store all train data to a file for examination, not used normally
Definition: TrainUnit.cpp:15204
TOnePrefDir::PrefDirSize
unsigned int PrefDirSize() const
Return the vector size.
Definition: TrackUnit.h:1374
TTrack::PlotSignal
void PlotSignal(int Caller, TTrackElement TrackElement, TDisplay *Disp)
Plot signals on screen according to their aspect (Attribute value)
Definition: TrackUnit.cpp:6079
End
@ End
Definition: TrackUnit.h:75
TTrain::OneLengthAccelDecel
bool OneLengthAccelDecel
set when a train can only move forwards one element before stopping but needs to accelerate for the f...
Definition: TrainUnit.h:397
TTrain::FloatingLabelNextString
AnsiString FloatingLabelNextString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the 'Next' action.
Definition: TrainUnit.cpp:6982
TTrack::TimetabledLocationNameAllocated
bool TimetabledLocationNameAllocated(int Caller, AnsiString LocationName)
True if a non-empty LocationName found as a timetabled location name i.e. not as a continuation name.
Definition: TrackUnit.cpp:8775
TTrain::FailedTrainNoFinishJoinMessage
bool FailedTrainNoFinishJoinMessage
Definition: TrainUnit.h:341
TTrack::InactiveTrackElementAt
TTrackElement & InactiveTrackElementAt(int Caller, int At)
A range-checked version of InactiveTrackVector.at(At)
Definition: TrackUnit.cpp:10528
ExitRailway
@ ExitRailway
Definition: TrainUnit.h:67
TPrefDirElement::GetTrackVectorPosition
unsigned int GetTrackVectorPosition() const
Returns TrackVectorPosition.
Definition: TrackUnit.h:299
TTrain::StationStopCalculated
bool StationStopCalculated
used in calculating DistanceToStationStop for trains running early before they have reached the stop ...
Definition: TrainUnit.h:403
TTrainController::LoadSessionContinuationAutoSigEntries
void LoadSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
load ContinuationAutoSigEntries from a session file
Definition: TrainUnit.cpp:15564
TTrackElement
Basic track elements as implemented in the overall railway layout.
Definition: TrackUnit.h:124
TRailGraphics::Code_n
Graphics::TBitmap * Code_n
Definition: GraphicUnit.h:955
FailMissedNewService
@ FailMissedNewService
Definition: TrainUnit.h:42
TUtilities::Format96HHMMSS
AnsiString Format96HHMMSS(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm:ss where hh runs from 00 to 95 & resets when...
Definition: Utilities.cpp:788
TRailGraphics::smSolidBgnd
Graphics::TBitmap * smSolidBgnd
Definition: GraphicUnit.h:1005
clTRSBackground
#define clTRSBackground
Definition: GraphicUnit.h:303
TTrainController::OtherMissedEvents
int OtherMissedEvents
Definition: TrainUnit.h:838
TTrainDataEntry::Description
AnsiString Description
headcode is the first train's headcode, rest are calculated from repeat information; ServiceReference...
Definition: TrainUnit.h:209
TTrainController::EarlyExits
int EarlyExits
Definition: TrainUnit.h:827
TTrain::ExitSpeedHalf
double ExitSpeedHalf
speed when half way into the next element
Definition: TrainUnit.h:421
SignalPost
@ SignalPost
Definition: TrackUnit.h:65
TRailGraphics::Code_h
Graphics::TBitmap * Code_h
Definition: GraphicUnit.h:949
TTrain::LastActionTime
TDateTime LastActionTime
time of the last timetabled event, used to ensure at least a 30 second delay before the next action
Definition: TrainUnit.h:462
TDisplay::GetOutputLog1
TLabel * GetOutputLog1()
Return pointers to warning message logs (appear above the railway display during operation)
Definition: DisplayUnit.h:144
TAllRoutes::TRoute2MultiMapIterator
TRoute2MultiMap::iterator TRoute2MultiMapIterator
Definition: TrackUnit.h:1635
TTrain::TrainFailurePending
bool TrainFailurePending
set when failure due & takes effect when all PlotElements properly set, added at v2....
Definition: TrainUnit.h:349
FailMissedTerminate
@ FailMissedTerminate
Definition: TrainUnit.h:42
TTrainController::SecondPassActions
bool SecondPassActions(int Caller, bool GiveMessages, bool &TwoLocationFlag)
Carry out further detailed timetable consistency checks, return true for success.
Definition: TrainUnit.cpp:12083
TRailGraphics::Code9
Graphics::TBitmap * Code9
Definition: GraphicUnit.h:977
TTrainDataEntry::HeadCode
AnsiString HeadCode
Definition: TrainUnit.h:209
TTrain::TrainHasFailed
void TrainHasFailed(int Caller)
Called when there is a random train failure.
Definition: TrainUnit.cpp:5453
clNormalBackground
#define clNormalBackground
Definition: GraphicUnit.h:297
TActionVectorEntry::NonRepeatingShuttleLinkEntryPtr
TTrainDataEntry * NonRepeatingShuttleLinkEntryPtr
pointer used by shuttles for the non-shuttle train links, in & out, the corresponding non-shuttle lin...
Definition: TrainUnit.h:150
TTrainController::SPADRisks
int SPADRisks
Definition: TrainUnit.h:841
TTrainController::TLocServiceTimesVector
std::vector< TLocServiceTimes > TLocServiceTimesVector
Definition: TrainUnit.h:764
TTrainController::LocServiceTimesAtLocTimeSort
bool LocServiceTimesAtLocTimeSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:891
TTrain::EntrySpeed
double EntrySpeed
speed at which the train enters the next element
Definition: TrainUnit.h:419
TConfiguration
TConfiguration
< describes the type of track link. 'End' is used for both buffer stop and continuation entry/exit po...
Definition: TrackUnit.h:74
TTrain::TrainGone
bool TrainGone
set when train has left the railway, so it can be removed from the display at the next clock tick
Definition: TrainUnit.h:474
TTrackType
TTrackType
< describes the type of track element
Definition: TrackUnit.h:64
TTrainController::OnTimePasses
int OnTimePasses
Definition: TrainUnit.h:836
TTrainController::SigSHigh
bool SigSHigh
Definition: TrainUnit.h:801
TRailGraphics::smCaramel
Graphics::TBitmap * smCaramel
Definition: GraphicUnit.h:893
TGraphicElement::PlotOriginal
void PlotOriginal(int Caller, TDisplay *Disp)
Plot the original graphic on screen.
Definition: TrackUnit.cpp:1878
TExitInfo
Definition: TrainUnit.h:106
clBufferAttentionNeeded
#define clBufferAttentionNeeded
Definition: GraphicUnit.h:290
TTrain::StoppedAfterSPAD
bool StoppedAfterSPAD
Definition: TrainUnit.h:478
FailSPAD
@ FailSPAD
Definition: TrainUnit.h:40
TTrainController::OnTimeArrivals
int OnTimeArrivals
Definition: TrainUnit.h:834
clTrainFailedBackground
#define clTrainFailedBackground
Definition: GraphicUnit.h:304
TRailGraphics::Code_x
Graphics::TBitmap * Code_x
Definition: GraphicUnit.h:965
clFrontCodeSignaller
#define clFrontCodeSignaller
Definition: GraphicUnit.h:295
TimeCmdHeadCode
@ TimeCmdHeadCode
Definition: TrainUnit.h:66
Utilities
TUtilities * Utilities
Definition: Utilities.cpp:47
TFixedTrackPiece::SmallGraphicPtr
Graphics::TBitmap * SmallGraphicPtr
the track bitmap for display on the zoomed-out railway
Definition: TrackUnit.h:93
TActionVectorEntry::ArrivalTime
TDateTime ArrivalTime
Definition: TrainUnit.h:136
TTrain::StoppedAtBuffers
bool StoppedAtBuffers
Definition: TrainUnit.h:478
TTrainController::CreateTTAnalysisFile
bool CreateTTAnalysisFile(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir, bool ArrChecked, bool DepChecked, bool AtLocChecked, bool DirChecked, int ArrRange, int DepRange)
Generate a timetable analysis file in the 'Formatted Timetables' folder, return false if failed for a...
Definition: TrainUnit.cpp:16361
TRailGraphics::Code_b
Graphics::TBitmap * Code_b
Definition: GraphicUnit.h:943
TTrain::Mass
int Mass
in kg
Definition: TrainUnit.h:452
TRailGraphics::Code0
Graphics::TBitmap * Code0
Definition: GraphicUnit.h:968
TTrainController::ProcessOneTimetableLine
bool ProcessOneTimetableLine(int Caller, int Count, AnsiString OneLine, bool &EndOfFile, bool FinalCall, bool GiveMessages, bool CheckLocationsExistInRailway)
Carry out preliminary (mainly syntax) validity checks on a single timetable service entry and (if Fin...
Definition: TrainUnit.cpp:10357
TTrainController::TContinuationTrainExpectationEntry::IncrementalMinutes
int IncrementalMinutes
Repeat separation in minutes.
Definition: TrainUnit.h:735
TTrain::LeadExitPos
int LeadExitPos
Definition: TrainUnit.h:366
TTrain::FrontElementLength
int FrontElementLength
values associated with the element immediately in front of the train (speed in km/h,...
Definition: TrainUnit.h:450
TTimetableShuttleLinkType
TTimetableShuttleLinkType
Definition: TrainUnit.h:81
TRailGraphics::CodeP
Graphics::TBitmap * CodeP
Definition: GraphicUnit.h:993
Pass
@ Pass
Definition: TrainUnit.h:54
TTrain::SkipPtrValue
int SkipPtrValue
stores the pointer increment from first action in ActionVector for skipped actions when a departure i...
Definition: TrainUnit.h:375
TNumList
std::list< int > TNumList
a list of valid train exit TrackVector positions for 'Fer' entries
Definition: TrainUnit.h:93
TTrainController::DerailWarning
bool DerailWarning
Definition: TrainUnit.h:787
TTrainController::TContinuationTrainExpectationMultiMapPair
std::pair< TDateTime, TContinuationTrainExpectationEntry > TContinuationTrainExpectationMultiMapPair
a single multimap entry
Definition: TrainUnit.h:749
RouteForceCancelled
@ RouteForceCancelled
Definition: TrainUnit.h:44
EnRoute
@ EnRoute
Definition: TrainUnit.h:72
TTrain::IsTrainIDOnBridgeTrackPos23
bool IsTrainIDOnBridgeTrackPos23(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 2 & 3.
Definition: TrainUnit.cpp:3043
TTrack::DiagonalFouledByTrain
bool DiagonalFouledByTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber, int &TrainID)
As DiagonalFouledByRouteOrTrain (in TAllRoutes) but only checks for a train (may or may not be a rout...
Definition: TrackUnit.cpp:11251
TTrainController::TotLateDepMins
float TotLateDepMins
Definition: TrainUnit.h:818
TDisplay::ZoomOutFlag
bool ZoomOutFlag
true when zoomed-out
Definition: DisplayUnit.h:70
TRailGraphics::CodeC
Graphics::TBitmap * CodeC
Definition: GraphicUnit.h:980
TTrain::PlotElement
int PlotElement[4]
the TrackVectorPosition of the element where each of the 4 headcode characters is plotted (need to be...
Definition: TrainUnit.h:485
TTrainController::TServiceCallingLocsList
std::list< AnsiString > TServiceCallingLocsList
Used in determining train directions in timetable conflict analysis.
Definition: TrainUnit.h:767
TTrainController::TrainVector
TTrainVector TrainVector
vector containing all trains currently in the railway
Definition: TrainUnit.h:872
TTrack::ResetAllTrainIDElements
void ResetAllTrainIDElements(int Caller)
Track elements have members that indicates whether and on what track a train is present (TrainIDOnEle...
Definition: TrackUnit.cpp:7607
TRailGraphics::Code_l
Graphics::TBitmap * Code_l
Definition: GraphicUnit.h:953
TTrainController::TContinuationAutoSigEntry
< TTClockTime when last session saved - to prevent display of warning message on exit session if < 5 ...
Definition: TrainUnit.h:709
TUtilities::CallLogPop
void CallLogPop(int Caller)
pops the last entry off the call stack, throws an error if called when empty
Definition: Utilities.cpp:50
TTrain::IncrementalMinutes
int IncrementalMinutes
the number of minutes to increment by in repeat entries
Definition: TrainUnit.h:354
TTrain::MaximumSpeedLimit
static const int MaximumSpeedLimit
km/h
Definition: TrainUnit.h:317
TRailGraphics::CodeI
Graphics::TBitmap * CodeI
Definition: GraphicUnit.h:986
TTrainController::TotEarlyArrMins
float TotEarlyArrMins
values for performance file summary
Definition: TrainUnit.h:813
TTrain::WriteTrainToImage
void WriteTrainToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TTrainController::WriteTrainsToImage (called by TInterface::SaveOperatingImage1Click) to ad...
Definition: TrainUnit.cpp:8532
TRailGraphics::CodeL
Graphics::TBitmap * CodeL
Definition: GraphicUnit.h:989
TTrainDataEntry::MaxBrakeRate
double MaxBrakeRate
in metres/sec/sec
Definition: TrainUnit.h:211
FNSNonRepeatToShuttle
@ FNSNonRepeatToShuttle
Definition: TrainUnit.h:66
TDisplay::PlotOutput
void PlotOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot the graphic at screen position HPos & VPos.
Definition: DisplayUnit.cpp:85
TUtilities::SaveFileBool
void SaveFileBool(std::ofstream &OutFile, bool SaveBool)
stores '1' if the bool is true or '0' if false to the file, then a CR
Definition: Utilities.cpp:108
TTrainDataEntry::SignallerSpeed
int SignallerSpeed
in km/h for use when under signaller control
Definition: TrainUnit.h:221
TTrain::AbleToMove
bool AbleToMove(int Caller)
Indicates that a train is not prevented from moving - used to allow appropriate popup menu options wh...
Definition: TrainUnit.cpp:6836
TTrainController::MTBFHours
double MTBFHours
Mean time between failures in timetable clock hours.
Definition: TrainUnit.h:804
TOneRoute::ForceCancelRoute
void ForceCancelRoute(int Caller)
Cancel a route immediately if a train occupies it when travelling in the wrong direction (or occupies...
Definition: TrackUnit.cpp:17368
TTrain::TrainMode
TTrainMode TrainMode
mode of operation - either Timetable (running under timetable control) or Signaller (running under si...
Definition: TrainUnit.h:466
TTrainController::CheckNonRepeatingShuttleLinkTime
bool CheckNonRepeatingShuttleLinkTime(int Caller, TDateTime ReverseEventTime, TDateTime ForwardEventTime, int RepeatMins, int RepeatNumber)
The forward train is the finish shuttle entry 'Fns-sh', the reverse (new non-repeating service) time ...
Definition: TrainUnit.cpp:14873
TTrack::ThisNamedLocationLongEnoughForSplit
bool ThisNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName, int FirstNamedElementPos, int &SecondNamedElementPos, int &FirstNamedLinkedElementPos, int &SecondNamedLinkedElementPos)
See above under 'OneNamedLocationLongEnoughForSplit'.
Definition: TrackUnit.cpp:10669
TUtilities::Format96HHMM
AnsiString Format96HHMM(TDateTime DateTime)
formats a TDateTime into an AnsiString of the form hh:mm where hh runs from 00 to 95 & resets when it...
Definition: Utilities.cpp:807
TTrain::CallOnMaxSpeed
static const int CallOnMaxSpeed
km/h
Definition: TrainUnit.h:311
TTrain::CheckAndCancelRouteForWrongEndEntry
void CheckAndCancelRouteForWrongEndEntry(int Caller, int Element, int EntryPos)
Checks whether Element and EntryPos (where train is about to enter) is on an existing route (or cross...
Definition: TrainUnit.cpp:3251
TRailGraphics::CodeW
Graphics::TBitmap * CodeW
Definition: GraphicUnit.h:1000
TTrain::ReleaseTime
TDateTime ReleaseTime
Definition: TrainUnit.h:460
TTrain::GetTrainHeadCode
AnsiString GetTrainHeadCode(int Caller)
Returns the train headcode, taking account of the RepeatNumber.
Definition: TrainUnit.cpp:5043
TTrain::LowEntryValue
bool LowEntryValue(int EntryLink) const
Returns true if EntryLink is 1, 2, 4 or 7, in these circumstances the front of the train (i....
Definition: TrainUnit.cpp:2564
RearSplit
@ RearSplit
Definition: TrainUnit.h:52
TActionVectorEntry::FrontStartOrRepeatDigits
int FrontStartOrRepeatDigits
dual-purpose variables used for the TrackVectorPositions of the rear and front train starting element...
Definition: TrainUnit.h:134
SignallerControlStop
@ SignallerControlStop
Definition: TrainUnit.h:54
TTrack::TrackVector
TTrackVector TrackVector
Definition: TrackUnit.h:801
TTrain::HasTrainGone
bool HasTrainGone()
Check whether the train has left the railway, so that it can be removed from the display at the next ...
Definition: TrainUnit.h:659
TRailGraphics::Code_i
Graphics::TBitmap * Code_i
Definition: GraphicUnit.h:950
TTrainController::ExcessLCDownMins
float ExcessLCDownMins
total excess time in minutes over the 3 minutes barriers down allowance for level crossings
Definition: TrainUnit.h:811
TRailGraphics::Code5
Graphics::TBitmap * Code5
Definition: GraphicUnit.h:973
TTrackElement::CallingOnSet
bool CallingOnSet
Used for for signals only when a train is being called on - used to plot the position lights.
Definition: TrackUnit.h:134
TUtilities::SaveFileInt
void SaveFileInt(std::ofstream &OutFile, int SaveInt)
stores the int value to the file, then a CR
Definition: Utilities.cpp:121
TrainController
TTrainController * TrainController
the object pointer, one object only - created in InterfaceUnit
Definition: TrainUnit.cpp:54
TTrack::OneNamedLocationLongEnoughForSplit
bool OneNamedLocationLongEnoughForSplit(int Caller, AnsiString LocationName)
Definition: TrackUnit.cpp:10563
TTrain::BufferAtExit
bool BufferAtExit(int Caller, int Element, int Exitpos) const
True if Element is a buffer and Exitpos is the buffer end.
Definition: TrainUnit.cpp:2975
TTrain::IsTrainTerminating
bool IsTrainTerminating(int Caller)
True if train service terminates at its current location.
Definition: TrainUnit.cpp:6808
TTrainController::SecondPassMessage
void SecondPassMessage(bool GiveMessages, AnsiString Message)
Give a user message during timetable integrity checking if GiveMessages is true, ignore if false.
Definition: TrainUnit.cpp:14971
SignallerStepForward
@ SignallerStepForward
Definition: TrainUnit.h:54
TTrack::GetHLocMin
int GetHLocMin()
Definition: TrackUnit.h:867
TTrainController::CheckCrossReferencesAndSetData
bool CheckCrossReferencesAndSetData(int Caller, AnsiString SoughtHeadCode, AnsiString SeekingHeadCode, bool Shuttle, bool GiveMessages)
A timetable validation function where all service cross references are checked for validity and set p...
Definition: TrainUnit.cpp:13634
TDisplay::GetOutputLog5
TLabel * GetOutputLog5()
Definition: DisplayUnit.h:165
TTrainController::LateExits
int LateExits
Definition: TrainUnit.h:832
TTrainController::OpTimeToActUpdateCounter
unsigned int OpTimeToActUpdateCounter
<List of all ServiceRefs that have two or more same locations without a cdt between - loaded during S...
Definition: TrainUnit.h:856
TTrain::BeingCalledOn
bool BeingCalledOn
in course of being called on to a station
Definition: TrainUnit.h:381
TUtilities::CheckFileInt
bool CheckFileInt(std::ifstream &InFile, int Lowest, int Highest)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success
Definition: Utilities.cpp:238
TRailGraphics::ChangeForegroundColour2
void ChangeForegroundColour2(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
New function to do the same as the above but with fewer pixel changes - for use in LoadSession to avo...
Definition: GraphicUnit.cpp:3449
TRailGraphics::Code_e
Graphics::TBitmap * Code_e
Definition: GraphicUnit.h:946
TTrain::CalcTimeToAct
float CalcTimeToAct(int Caller, float &TimeToExit, THVShortPair &ExitPair)
new v2.2.0 for operator action panel. Calculates the time left for operator action to avoid unnecessa...
Definition: TrainUnit.cpp:8596
TTrain::HoldAtLocationInTTMode
bool HoldAtLocationInTTMode
true if actions are needed before train departs
Definition: TrainUnit.h:331
TTrain::PlotTrainInZoomOutMode
void PlotTrainInZoomOutMode(int Caller, bool Flash)
Plots the train on screen in zoomed-out mode, state of 'Flash' determines whether the flashing trains...
Definition: TrainUnit.cpp:8342
TTrain::LoadOneSessionTrain
void LoadOneSessionTrain(int Caller, std::ifstream &InFile)
Create one train with relevant member values from the sesion file.
Definition: TrainUnit.cpp:7621
TTrain::PlotBackgroundGraphic
void PlotBackgroundGraphic(int Caller, int ArrayNumber, TDisplay *Disp) const
Replot the graphic pointed to by BackgroundPtr (see above) after a train has passed.
Definition: TrainUnit.cpp:2967
TTrainController::TotLateArrMins
float TotLateArrMins
Definition: TrainUnit.h:817
TTrain::HOffset
int HOffset[4]
Definition: TrainUnit.h:481
TTrain::GetNewServiceDepartureInfo
AnsiString GetNewServiceDepartureInfo(int Caller, TActionVectorEntry *Ptr, int RptNum, TTrainDataEntry *LinkedTrainDataPtr, AnsiString RetStr)
called during FloatingLabelNextString to find the next service departure time & next location
Definition: TrainUnit.cpp:7112
FailMissedArrival
@ FailMissedArrival
Definition: TrainUnit.h:41
TTrain::UpdateCounter
unsigned int UpdateCounter
used in train splitting operations to prevent too frequent checks for a location being long enough fo...
Definition: TrainUnit.h:456
NewService
@ NewService
Definition: TrainUnit.h:52
FailEnterLockedRoute
@ FailEnterLockedRoute
Definition: TrainUnit.h:42
TTrainController::PlotAllTrainsInZoomOutMode
void PlotAllTrainsInZoomOutMode(int Caller, bool Flash)
Plots all trains on screen in zoomed-out mode, state of 'Flash' determines whether the flashing train...
Definition: TrainUnit.cpp:15717
TOneRoute
A descendent of TOnePrefDir used for routes. Used during contruction of a route (ConstructRoute) and ...
Definition: TrackUnit.h:1474
TTrain::SignallerStopped
bool SignallerStopped
Definition: TrainUnit.h:478
TTrain::BufferZoomOutFlashRequired
bool BufferZoomOutFlashRequired
set when train is at buffers and is to flash in zoomout mode (i.e. when reaches buffers unexpectedly ...
Definition: TrainUnit.h:383
TAllRoutes::TRouteType
TRouteType
Definition: TrackUnit.h:1618
TTrain::TrainDataEntryPtr
TTrainDataEntry * TrainDataEntryPtr
points to the current position in the timetable's TrainDataVector
Definition: TrainUnit.h:371
TTrainDataEntry::StartSpeed
int StartSpeed
in km/h
Definition: TrainUnit.h:223
TTrainDataEntry::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:219
TTrainController::Derailments
int Derailments
Definition: TrainUnit.h:824
clStationStopBackground
#define clStationStopBackground
Definition: GraphicUnit.h:301
TDisplay::GetOutputLog6
TLabel * GetOutputLog6()
Definition: DisplayUnit.h:170
ShuttleLink
@ ShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::CodeM
Graphics::TBitmap * CodeM
Definition: GraphicUnit.h:990
Crossover
@ Crossover
Definition: TrackUnit.h:65
TTrain::ClearToNextSignal
bool ClearToNextSignal(int Caller)
Checks forward from train LeadElement, following leading point attributes but ignoring trailing point...
Definition: TrainUnit.cpp:4643
TTrainController::TContinuationTrainExpectationEntry::Description
AnsiString Description
service description
Definition: TrainUnit.h:729
AtLocation
@ AtLocation
Definition: TrainUnit.h:72
Signal
@ Signal
Definition: TrackUnit.h:75
TRailGraphics::Code_k
Graphics::TBitmap * Code_k
Definition: GraphicUnit.h:952
TRailGraphics::CodeF
Graphics::TBitmap * CodeF
Definition: GraphicUnit.h:983
TTrack::ContinuationNameMap
std::map< AnsiString, char > ContinuationNameMap
map of all continuation names, char is a dummy
Definition: TrackUnit.h:772
TTrackElement::TrainIDOnBridgeTrackPos01
int TrainIDOnBridgeTrackPos01
Definition: TrackUnit.h:152
TAllRoutes::GetRouteTypeAndNumber
TRouteType GetRouteTypeAndNumber(int Caller, int TrackVectorPosition, int LinkPos, int &RouteNumber)
Examines Route2MultiMap and if the element at TrackVectorPosition with LinkPos (can be entry or exit)...
Definition: TrackUnit.cpp:17885
TTrainController::TrainDataVector
TTrainDataVector TrainDataVector
vector containing the internal timetable
Definition: TrainUnit.h:870
TTrainController::ConsolidateSARNTAtLoc
AnsiString ConsolidateSARNTAtLoc(int Caller, const AnsiString Input, int &NumTrainsAtLoc)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for AtLoc listing ...
Definition: TrainUnit.cpp:18739
TAllRoutes::NotAutoSigsRoute
@ NotAutoSigsRoute
Definition: TrackUnit.h:1619
TTrain::ExitTimeHalf
TDateTime ExitTimeHalf
Definition: TrainUnit.h:458
Exited
@ Exited
Definition: TrainUnit.h:88
TTrack::TrackElementAt
TTrackElement & TrackElementAt(int Caller, int At)
A range-checked version of TrackVector.at(At)
Definition: TrackUnit.cpp:10514
TTrain::ActionsSkippedFlag
bool ActionsSkippedFlag
prevents any further skipping until after the next departure
Definition: TrainUnit.h:329
TTrainController::LoadSessionTrains
void LoadSessionTrains(int Caller, std::ifstream &SessionFile)
load trains from a session file
Definition: TrainUnit.cpp:15415
TTrain::CheckOneSessionTrain
static bool CheckOneSessionTrain(std::ifstream &InFile)
Carries out an integrity check for the train section of a session file, if fails a message is given a...
Definition: TrainUnit.cpp:7908
TAllRoutes::GetRouteTypeAndGraphics
TRouteType GetRouteTypeAndGraphics(int Caller, int TrackVectorPosition, int LinkPos, Graphics::TBitmap *&EXGraphicPtr, Graphics::TBitmap *&EntryDirectionGraphicPtr)
Examines Route2MultiMap for the element at TrackVectorPosition with LinkPos (can be entry or exit).
Definition: TrackUnit.cpp:17711
TRailGraphics::CodeY
Graphics::TBitmap * CodeY
Definition: GraphicUnit.h:1002
TTrain::SetOneGraphicCode
Graphics::TBitmap * SetOneGraphicCode(char CodeChar)
Return a pointer to the graphic corresponding to the character 'CodeVhar'.
Definition: TrainUnit.cpp:2181
TTrain::DerailPending
bool DerailPending
Definition: TrainUnit.h:478
TTrainMode
TTrainMode
indicates train operating mode, 'None' for not in use
Definition: TrainUnit.h:59
TAllRoutes::TCallonEntry
Used to store relevant values when a call-on found, ready for plotting an unrestricted route.
Definition: TrackUnit.h:1640
TRailGraphics::Code_a
Graphics::TBitmap * Code_a
Definition: GraphicUnit.h:942
TTrain::StoppedAtLocation
bool StoppedAtLocation
Definition: TrainUnit.h:478
TTrainController::TContinuationTrainExpectationMultiMapIterator
TContinuationTrainExpectationMultiMap::iterator TContinuationTrainExpectationMultiMapIterator
iterator for the multimap
Definition: TrainUnit.h:747
TTrain::MaximumMassLimit
static const int MaximumMassLimit
kg (i.e. 10,000 tonnes)
Definition: TrainUnit.h:313
TTrainController::StopTTClockFlag
bool StopTTClockFlag
when true the timetable clock is stopped, used for messages display and train popup menu display etc
Definition: TrainUnit.h:789
FrontSplit
@ FrontSplit
Definition: TrainUnit.h:52
TTrain::MaxExitSpeed
double MaxExitSpeed
the maximum speed that the train can exit the next element
Definition: TrainUnit.h:429
TTrainController::BFHigh
bool BFHigh
Definition: TrainUnit.h:801
TRailGraphics::Code4
Graphics::TBitmap * Code4
Definition: GraphicUnit.h:972
TRailGraphics::Code_g
Graphics::TBitmap * Code_g
Definition: GraphicUnit.h:948
TTrain::LagEntryPos
int LagEntryPos
Definition: TrainUnit.h:366
SNTShuttle
@ SNTShuttle
Definition: TrainUnit.h:66
TRailGraphics::CodeG
Graphics::TBitmap * CodeG
Definition: GraphicUnit.h:984
Leave
@ Leave
Definition: TrainUnit.h:52
TTrainController::UnexpectedExits
int UnexpectedExits
Definition: TrainUnit.h:843
TTrainController::TLocServiceTimes::AtLocTime
AnsiString AtLocTime
Definition: TrainUnit.h:759
TTrainController::ServiceReference
AnsiString ServiceReference
String used to display the offending service in timetable error messages.
Definition: TrainUnit.h:785
TTrain::FirstLaterStopRecoverableTime
float FirstLaterStopRecoverableTime
this used to deduct from RecoverableTime when arrive at a location for OperatorActionpanel
Definition: TrainUnit.h:448
TTrain::LeavingUnderSigControlAtContinuation
bool LeavingUnderSigControlAtContinuation
set when the train has reached an exit continuation when under signaller control, used to prevent the...
Definition: TrainUnit.h:395
TTrain::PlotTrainGraphic
void PlotTrainGraphic(int Caller, int ArrayNumber, TDisplay *Disp)
Plot the train's headcode character corresponding to ArrayNumber.
Definition: TrainUnit.cpp:2948
TRailGraphics::Code_j
Graphics::TBitmap * Code_j
Definition: GraphicUnit.h:951
NoFormat
@ NoFormat
Definition: TrainUnit.h:66
TTrainController::TwoOrMoreLocationsWarningGiven
bool TwoOrMoreLocationsWarningGiven
new at v2.6.0 to allow loops
Definition: TrainUnit.h:797
FailBufferCrash
@ FailBufferCrash
Definition: TrainUnit.h:43
WaitingForFJO
@ WaitingForFJO
Definition: TrainUnit.h:43
FailMissedExitRailway
@ FailMissedExitRailway
Definition: TrainUnit.h:42
TimeTimeLoc
@ TimeTimeLoc
Definition: TrainUnit.h:66
TTrain::GetOffsetValues
void GetOffsetValues(int Caller, int &HOffset, int &VOffset, int Link) const
Sets HOffset & VOffset (see above) for a single headcode character depending on the Link value.
Definition: TrainUnit.cpp:2493
TTrain::TimeToExit
float TimeToExit
in minutes: new for multiplayer, -1 = > 60 mins
Definition: TrainUnit.h:446
TTrain::Derailed
bool Derailed
Definition: TrainUnit.h:478
TRailGraphics::ChangeSpecificColour
void ChangeSpecificColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor ColourToBeChanged, TColor NewColour)
Definition: GraphicUnit.cpp:3478
Enter
@ Enter
Definition: TrainUnit.h:52
TRailGraphics::bm93set
Graphics::TBitmap * bm93set
Definition: GraphicUnit.h:514
TTrack::GetTrackVectorPositionFromString
int GetTrackVectorPositionFromString(int Caller, AnsiString String, bool GiveMessages)
Takes the ElementID value (an AnsiString) (e.g. "8-13", "N43-N127", etc) and returns the correspondin...
Definition: TrackUnit.cpp:7822
TRailGraphics::Code_q
Graphics::TBitmap * Code_q
Definition: GraphicUnit.h:958
TTrainController::SaveSessionLockedRoutes
void SaveSessionLockedRoutes(int Caller, std::ofstream &SessionFile)
save locked routes to a session file
Definition: TrainUnit.cpp:15464
TTrain::OriginalPowerAtRail
double OriginalPowerAtRail
new at v2.4.0 to store value before a failure so it can be restored from here when repaired
Definition: TrainUnit.h:440
TTrainFormattedInformation
Contains all information for a single timetable entry for use in the formatted timetable.
Definition: TrainUnit.h:276
TTrainController::IsSNTEntryLocated
bool IsSNTEntryLocated(int Caller, const TTrainDataEntry &TDEntry, AnsiString &LocationName)
New trains introduced with 'Snt' may be at a timetabled location or elsewhere. This function checks a...
Definition: TrainUnit.cpp:14164
TTrainController::TContinuationTrainExpectationEntry::VectorPosition
int VectorPosition
TrackVectorPosition for the continuation element.
Definition: TrainUnit.h:739
TTrain::NewTrainService
void NewTrainService(int Caller, bool NoLogFlag)
Carry out the actions needed when a train forms a new service (code Fns)
Definition: TrainUnit.cpp:6322
TTrainController::TTrainController
TTrainController()
Constructor.
Definition: TrainUnit.cpp:9026
Timetable
@ Timetable
Definition: TrainUnit.h:60
TUtilities::SaveFileDouble
void SaveFileDouble(std::ofstream &OutFile, double SaveDouble)
converts the double value to a string (if double stored directly it is truncated to 6 digits) then st...
Definition: Utilities.cpp:127
TTrainController::GetRepeatHeadCode
AnsiString GetRepeatHeadCode(int Caller, AnsiString BaseHeadCode, int RepeatNumber, int IncDigits)
Return the service headcode for the repeat service.
Definition: TrainUnit.cpp:14543
TTrain::AbleToMoveButForSignal
bool AbleToMoveButForSignal(int Caller)
Indicates that a train is only prevented from moving by a signal - used to allow appropriate popup me...
Definition: TrainUnit.cpp:6893
TTrainController::OpActionPanelVisible
bool OpActionPanelVisible
new v2.2.0 flag to prevent time to act functions when not visible
Definition: TrainUnit.h:795
TTrack::GetAnyElementOppositeLinkPos
int GetAnyElementOppositeLinkPos(int Caller, int TrackVectorPosition, int LinkPos, bool &Derail)
Return the opposite link position for the element at TrackVectorPosition with link position LinkPos,...
Definition: TrackUnit.cpp:11102
TTrack::GapFlashRedPosition
int GapFlashRedPosition
TrackVectorPosition of the gap element that is flashing green or red.
Definition: TrackUnit.h:764
TActionVectorEntry::NumberOfRepeats
int NumberOfRepeats
the number of repeating services
Definition: TrainUnit.h:132
TAllRoutes::GetFixedRouteAt
const TOneRoute & GetFixedRouteAt(int Caller, int At) const
Returns a constant reference to the route at AllRoutesVector position 'At', after performing range ch...
Definition: TrackUnit.cpp:17554
TUtilities::clTransparent
TColor clTransparent
the display background colour, can be white, black or dark blue
Definition: Utilities.h:65
TTrainController::SignalStopWarning
bool SignalStopWarning
Definition: TrainUnit.h:787
TPrefDirElement::GetELinkPos
int GetELinkPos() const
Returns the ELink array position.
Definition: TrackUnit.h:269
TTrainController::CalcOperatingAndNotStartedTrainLateness
void CalcOperatingAndNotStartedTrainLateness(int Caller)
calculates additional lateness values for trains that haven't reached their destinations yet
Definition: TrainUnit.cpp:19680
TDisplay::PerformanceLog
void PerformanceLog(int Caller, AnsiString Statement)
Send Statement to the performance log on screen and to the file.
Definition: DisplayUnit.cpp:521
ShuttleLinkTypeForRepeatEntry
@ ShuttleLinkTypeForRepeatEntry
Definition: TrainUnit.h:82
TTrain::SignallerStopBrakeRate
double SignallerStopBrakeRate
the train brake rate when stopping under signaller control
Definition: TrainUnit.h:435
TTrainController::TLocServiceTimes::Location
AnsiString Location
Definition: TrainUnit.h:757
TOneCompleteFormattedTrain
A single train with its headcode + list of actions for use in the formatted timetable.
Definition: TrainUnit.h:263
TTrainController::SignallerTrainRemovedOnAutoSigsRoute
bool SignallerTrainRemovedOnAutoSigsRoute
true if train was on an AutoSigsRoute when removed by the signaller
Definition: TrainUnit.h:793
Terminate
@ Terminate
Definition: TrainUnit.h:52
TTrainController::MRSLow
bool MRSLow
Definition: TrainUnit.h:801
TTrack::ActiveTrackElementNameMap
TActiveTrackElementNameMap ActiveTrackElementNameMap
< map of coupled continuations
Definition: TrackUnit.h:776
TTrain::Crashed
bool Crashed
Definition: TrainUnit.h:478
TTrackElement::HLoc
int HLoc
Definition: TrackUnit.h:146
TTrain::HeadCodePosition
Graphics::TBitmap * HeadCodePosition[4]
Set from the HeadCodeGrPtr[4] pointer values, HeadCodePosition[0] is always the front,...
Definition: TrainUnit.h:492
TTrain::TrainID
int TrainID
the train's identification number
Definition: TrainUnit.h:368
Running
@ Running
Definition: TrainUnit.h:88
TAllFormattedTrains
std::vector< TTrainFormattedInformation > TAllFormattedTrains
vector of all timetabled trains for use in the formatted timetable
Definition: TrainUnit.h:285
TRailGraphics::ChangeBackgroundColour3
void ChangeBackgroundColour3(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewBackgroundColour, TColor OldBackgroundColour)
as above but uses Scanline
Definition: GraphicUnit.cpp:3575
TRailGraphics::smCyan
Graphics::TBitmap * smCyan
Definition: GraphicUnit.h:894
TTrainController::MovingSuccessor
bool MovingSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13506
TTrainController::LogActionError
void LogActionError(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionEventType ActionEventType, AnsiString LocationID)
Send an error message to the performance log and file, and as a warning if appropriate.
Definition: TrainUnit.cpp:14983
TTrainController::TrainVectorAt
TTrain & TrainVectorAt(int Caller, int VecPos)
Return a reference to the train at position VecPos in the TrainVector, carries out range checking on ...
Definition: TrainUnit.cpp:15740
TTrain::FrontElementSpeedLimit
int FrontElementSpeedLimit
Definition: TrainUnit.h:450
TTrack::NumberOfPlatforms
int NumberOfPlatforms(int Caller, AnsiString LocationName)
Returns the number of separate platforms (not platform elements) at a given location,...
Definition: TrackUnit.cpp:11322
TTrack::TSigElement::SpeedTag
int SpeedTag
the TrackElement SpeedTag value - specifies the signal element
Definition: TrackUnit.h:709
TTrain::DeleteTrain
void DeleteTrain(int Caller)
This is a housekeeping function to delete train heap objects (bitmaps) explicitly rather than by usin...
Definition: TrainUnit.cpp:233
TTrainController::NumFailures
int NumFailures
Definition: TrainUnit.h:844
TTrain::ExitSpeedFull
double ExitSpeedFull
speed when leaving the next element
Definition: TrainUnit.h:423
TTrain::SetHeadCodeGraphics
void SetHeadCodeGraphics(int Caller, AnsiString Code)
Set the four HeadCodeGrPtr[4] pointers to the appropriate character graphics with the current backgro...
Definition: TrainUnit.cpp:2378
TAllRoutes::SetTrailingSignalsOnAutoSigsRoute
void SetTrailingSignalsOnAutoSigsRoute(int Caller, int TrackVectorPosition, int XLinkPos)
Enter with signal at TrackVectorElement already set to red by the passing train.
Definition: TrackUnit.cpp:18569
clB5G5R5
#define clB5G5R5
Definition: GraphicUnit.h:286
TUtilities::TimeStamp
AnsiString TimeStamp()
creates a string of the form 'hh:mm:ss' for use in call & event logging
Definition: Utilities.cpp:73
TTrainController::RandomFailureCounter
unsigned int RandomFailureCounter
new at v2.4.0, resets after 53 seconds (53 prime so can trigger at any clock time)
Definition: TrainUnit.h:860
TAllRoutes::TLockedRouteClass::RouteNumber
int RouteNumber
the vector position number of the relevant route in AllRoutesVector
Definition: TrackUnit.h:1605
TTrainController::AddTrain
bool AddTrain(int Caller, int RearPosition, int FrontPosition, AnsiString HeadCode, int StartSpeed, int Mass, double MaxRunningSpeed, double MaxBrakeRate, double PowerAtRail, AnsiString ModeStr, TTrainDataEntry *TrainDataEntryPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits, int SignallerSpeed, bool SignallerControl, TActionEventType &EventType)
Introduce a new train to the railway, with the characteristics specified, returns true for success,...
Definition: TrainUnit.cpp:9506
TTrainController::~TTrainController
~TTrainController()
Destructor.
Definition: TrainUnit.cpp:9079
TTrain::RepeatShuttleOrNewNonRepeatService
void RepeatShuttleOrNewNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6735
TTrainController::TwoLocationList
TServiceCallingLocsList TwoLocationList
Definition: TrainUnit.h:854
TUtilities::CheckAndReadFileInt
bool CheckAndReadFileInt(std::ifstream &InFile, int Lowest, int Highest, int &OutInt)
checks that the value is an int lying between Lowest & Highest (inclusive), returns true for success ...
Definition: Utilities.cpp:280
TRailGraphics::Code7
Graphics::TBitmap * Code7
Definition: GraphicUnit.h:975
TTrackElement::ActiveTrackElementName
AnsiString ActiveTrackElementName
Location name used either in the timetable or for a continuation (continuation names not used in time...
Definition: TrackUnit.h:127
TRailGraphics::bm94set
Graphics::TBitmap * bm94set
Definition: GraphicUnit.h:516
TTrainController::LogEvent
void LogEvent(AnsiString Str)
store Str to the event log - moved from TUtilities for v0.6 so can record the tt clock value
Definition: TrainUnit.cpp:9090
TRailGraphics::CodeO
Graphics::TBitmap * CodeO
Definition: GraphicUnit.h:992
TTrain::ExitPair
THVShortPair ExitPair
H & V coordinates of the exit element related to TimeToExit, new for multiplayer.
Definition: TrainUnit.h:464
TAllRoutes::SignallerRemovedTrainAutoRoute
TOneRoute SignallerRemovedTrainAutoRoute
if train was on an AutoSigsRoute when removed then this stores the route so that signals can be reset
Definition: TrackUnit.h:1692
TTrainController::MassHigh
bool MassHigh
Definition: TrainUnit.h:801
TTrainController::CheckAndPopulateListOfIDs
bool CheckAndPopulateListOfIDs(int Caller, AnsiString IDSet, TNumList &ExitList, bool GiveMessages)
Used to compile ExitList from a string list of element IDs, returns true for success or gives a messa...
Definition: TrainUnit.cpp:11500
TTrain::PlotTrainWithNewBackgroundColour
void PlotTrainWithNewBackgroundColour(int Caller, TColor NewBackgroundColour, TDisplay *Disp)
Changes the train's background colour (e.g. to pale green if stopped at a station) Note that this use...
Definition: TrainUnit.cpp:3447
TUtilities::SaveFileString
void SaveFileString(std::ofstream &OutFile, AnsiString SaveString)
stores the string value to the file, then a '0' delimiter then a CR
Definition: Utilities.cpp:135
TTrain::PlotAlternativeTrackRouteGraphic
void PlotAlternativeTrackRouteGraphic(int Caller, unsigned int LagElement, int LagELinkPos, int HOffset, int VOffset, TStraddle StraddleValue)
When a train moves off a bridge the other track may contain a route or have a train on it that has be...
Definition: TrainUnit.cpp:3147
TTrain::ExitTimeFull
TDateTime ExitTimeFull
times used in SetTrainMovementValues corresponding to the next element the train runs on
Definition: TrainUnit.h:458
TTrainController::CalcDistanceToRedSignalandStopTime
int CalcDistanceToRedSignalandStopTime(int Caller, int TrackVectorPosition, int TrackVectorPositionEntryPos, bool SigControlAndCanPassRedSignal, TActionVectorEntry *AVPtr, AnsiString HeadCode, int TrainID, float &CurrentStopTime, float &LaterStopTime, float &RecoverableTime, int &AvTrackSpeed, int &DistanceToExit, THVShortPair &ExitPair)
new v2.2.0 (DistanceToExit added for multiplayer), calcs distances to red signal & exit,...
Definition: TrainUnit.cpp:19993
TTrack::GapFlashRed
TGraphicElement * GapFlashRed
the red & green circle graphics used to show where the gaps are
Definition: TrackUnit.h:784
LocTypeForRepeatEntry
@ LocTypeForRepeatEntry
Definition: TrainUnit.h:72
SignallerChangeDirection
@ SignallerChangeDirection
Definition: TrainUnit.h:54
TAllRoutes::TLockedRouteClass
Handles routes that are locked because of approaching trains.
Definition: TrackUnit.h:1603
TTrain::TrainAtLocation
bool TrainAtLocation(int Caller, AnsiString &LocationName)
True when the train is stopped at a timetabled location.
Definition: TrainUnit.cpp:8489
TTrain::IsTrainIDOnBridgeTrackPos01
bool IsTrainIDOnBridgeTrackPos01(int Caller, unsigned int TrackVectorPosition)
True if train is on a bridge on trackpos 0 & 1.
Definition: TrainUnit.cpp:3011
TActionVectorEntry
Contains a single train action in a timetable - repeat entry is also of this class though no train ac...
Definition: TrainUnit.h:122
TTrainController::TotLateExitMins
float TotLateExitMins
Definition: TrainUnit.h:820
TTrainController::CheckSessionContinuationAutoSigEntries
bool CheckSessionContinuationAutoSigEntries(int Caller, std::ifstream &SessionFile)
Part of the session file integrity check for ContinuationAutoSigEntries, true for success.
Definition: TrainUnit.cpp:15586
TOneCompleteFormattedTrain::OneFormattedTrainVector
TOneFormattedTrainVector OneFormattedTrainVector
Definition: TrainUnit.h:266
TRailGraphics::Code1
Graphics::TBitmap * Code1
Definition: GraphicUnit.h:969
TTrainController::TimetableMessage
void TimetableMessage(bool GiveMessages, AnsiString Message)
Sends a message to the user if GiveMessages is true, including ServiceReference (see above) if not nu...
Definition: TrainUnit.cpp:14950
TDisplay
Definition: DisplayUnit.h:48
TTrackElement::ConnLinkPos
int ConnLinkPos[4]
Connecting element link position (i.e. array positions of the connecting element links,...
Definition: TrackUnit.h:144
TTrainController::ContinuationAutoSigVector
TContinuationAutoSigVector ContinuationAutoSigVector
vector for TContinuationAutoSigEntry objects
Definition: TrainUnit.h:862
TTrain::StoppedWithoutPower
bool StoppedWithoutPower
Definition: TrainUnit.h:479
TTrain::NameInTimetableBeforeCDT
int NameInTimetableBeforeCDT(int Caller, AnsiString Name, bool &Stop)
Returns the number by which the train ActionVectorEntryPtr needs to be incremented to point to the lo...
Definition: TrainUnit.cpp:4580
TTrainFormattedInformation::Header
AnsiString Header
description, mass, power, brake rate etc
Definition: TrainUnit.h:278
TActionEventType
TActionEventType
Used for reporting error conditions & warnings.
Definition: TrainUnit.h:39
TTrainController::TrainExistsAtIdent
bool TrainExistsAtIdent(int Caller, int TrainID)
new at v2.4.0 return true if find the train (added at v2.4.0 as can select a removed train in OAListB...
Definition: TrainUnit.cpp:9840
SignallerJoin
@ SignallerJoin
Definition: TrainUnit.h:53
TTrain::RearStartElement
int RearStartElement
start TrackVectorPosition element for rear of train
Definition: TrainUnit.h:356
TTrain::StepForwardFlag
bool StepForwardFlag
set when the signaller command to step forward one element has been given
Definition: TrainUnit.h:405
TTrainController::SplitTrainInfo
bool SplitTrainInfo(int Caller, AnsiString TrainInfoStr, AnsiString &HeadCode, AnsiString &Description, int &StartSpeed, int &MaxRunningSpeed, int &Mass, double &MaxBrakeRate, double &PowerAtRail, int &SignallerSpeed, bool GiveMessages)
Parse a train information entry, return true for success; PowerAtRail changed to double& from int& at...
Definition: TrainUnit.cpp:11583
TTrainController::TotEarlyExitMins
float TotEarlyExitMins
Definition: TrainUnit.h:816
TFixedTrackPiece::Config
TConfiguration Config[4]
the type of link - see TConfiguration above
Definition: TrackUnit.h:96
TTrainController::Operate
void Operate(int Caller)
called every clock tick to introduce new trains and update existing trains
Definition: TrainUnit.cpp:9104
TTrackElement::TrainIDOnElement
int TrainIDOnElement
Definition: TrackUnit.h:152
TTrainController::SkippedTTEvents
int SkippedTTEvents
Definition: TrainUnit.h:839
TRailGraphics::smRed
Graphics::TBitmap * smRed
Definition: GraphicUnit.h:901
TTrain::PowerAtRail
double PowerAtRail
in Watts (taken as 80% of the train's Gross Power, i.e. that entered by the user)
Definition: TrainUnit.h:438
TRailGraphics::LCPlainMan
Graphics::TBitmap * LCPlainMan
Definition: GraphicUnit.h:745
TDisplay::GetOutputLog4
TLabel * GetOutputLog4()
Definition: DisplayUnit.h:160
TAllRoutes::RebuildRailwayFlag
bool RebuildRailwayFlag
this is set whenever a route has to be cancelled forcibly in order to force a ClearandRebuildRailway ...
Definition: TrackUnit.h:1673
TTrainDataEntry::MaxRunningSpeed
double MaxRunningSpeed
in km/h
Definition: TrainUnit.h:213
TUtilities::LoadFileInt
int LoadFileInt(std::ifstream &InFile)
loads an int value from the file
Definition: Utilities.cpp:162
TTrackElement::ElementID
AnsiString ElementID
the element identifier based on position in the railway
Definition: TrackUnit.h:129
TTimeToExitMultiMapEntry
std::pair< THVShortPair, TExitInfo > TTimeToExitMultiMapEntry
Definition: TrainUnit.h:116
TRailGraphics::Code6
Graphics::TBitmap * Code6
Definition: GraphicUnit.h:974
TTrainDataEntry::TrainOperatingDataVector
TTrainOperatingDataVector TrainOperatingDataVector
operating information for the train including all its repeats
Definition: TrainUnit.h:227
TTrackElement::VLoc
int VLoc
The h & v locations in the railway (top lh corner of the first build screen = 0,0)
Definition: TrackUnit.h:146
TTrainController::OperatingTrainLateMins
float OperatingTrainLateMins
total late minutes of operating trains on exit operation for locations not reached yet
Definition: TrainUnit.h:809
TTrack::GetNonPointsOppositeLinkPos
int GetNonPointsOppositeLinkPos(int LinkPosIn)
Return the corresponding link position (track always occupies either links 0 & 1 or 2 & 3)
Definition: TrackUnit.h:883
TTrain::UnplotTrain
void UnplotTrain(int Caller)
Unplot train from screen in zoomed-in mode.
Definition: TrainUnit.cpp:552
TTrain::IsThereAnAdjacentTrain
bool IsThereAnAdjacentTrain(int Caller, TTrain *&TrainToBeJoinedBy)
Definition: TrainUnit.cpp:5066
TTrain::TRSTime
TDateTime TRSTime
location departure time and 'train ready to start' time (TRSTime is 10 seconds before the ReleaseTime...
Definition: TrainUnit.h:460
TAllRoutes::TLockedRouteClass::LastTrackVectorPosition
unsigned int LastTrackVectorPosition
the TrackVector position of the last (i.e. most forward) element in the route
Definition: TrackUnit.h:1609
FailMissedJBO
@ FailMissedJBO
Definition: TrainUnit.h:41
TUtilities::EventLog
std::deque< AnsiString > EventLog
event store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:57
TAllRoutes::NoRoute
@ NoRoute
Definition: TrackUnit.h:1619
TActionVectorEntry::FormatType
TTimetableFormatType FormatType
defines the timetable action type
Definition: TrainUnit.h:140
TTrain::RemainHereLogNotSent
bool RemainHereLogNotSent
flag to prevent repeated logs, new at v1.2.0
Definition: TrainUnit.h:335
TRailGraphics::CodeU
Graphics::TBitmap * CodeU
Definition: GraphicUnit.h:998
TTrainController::RestartTime
TDateTime RestartTime
TTClockTime when operation pauses ( = timetable start time prior to operation) TTClockTime is calcula...
Definition: TrainUnit.h:702
TTrainController::TimetableStartTime
TDateTime TimetableStartTime
the start time of the current timetable
Definition: TrainUnit.h:700
clSPADBackground
#define clSPADBackground
Definition: GraphicUnit.h:300
TTrainController::UnplotTrains
void UnplotTrains(int Caller)
unplot all trains from screen
Definition: TrainUnit.cpp:9490
TRailGraphics::smBrightGreen
Graphics::TBitmap * smBrightGreen
Definition: GraphicUnit.h:892
TTrainController::EarlyArrivals
int EarlyArrivals
Definition: TrainUnit.h:825
FailCreateTrain
@ FailCreateTrain
Definition: TrainUnit.h:40
TTrain::IncrementalDigits
int IncrementalDigits
the number of digits to increment by in repeat entries
Definition: TrainUnit.h:352
TTrainController::CreateFormattedTimetable
void CreateFormattedTimetable(int Caller, AnsiString RailwayTitle, AnsiString TimetableTitle, AnsiString CurDir)
Examines the internal timetable (TrainDataVector) and creates from it a chronological (....
Definition: TrainUnit.cpp:15753
TTrack::RouteFlashFlag
bool RouteFlashFlag
true while a route is flashing prior to being set
Definition: TrackUnit.h:748
TRailGraphics::Code_v
Graphics::TBitmap * Code_v
Definition: GraphicUnit.h:963
TTrackElement::StationEntryStopLinkPos1
int StationEntryStopLinkPos1
Definition: TrackUnit.h:150
Points
@ Points
Definition: TrackUnit.h:65
TTrainController::SplitEntry
bool SplitEntry(int Caller, AnsiString OneEntry, bool GiveMessages, bool CheckLocationsExistInRailway, AnsiString &First, AnsiString &Second, AnsiString &Third, AnsiString &Fourth, int &RearStartOrRepeatMins, int &FrontStartPosition, TTimetableFormatType &TimetableFormatType, TTimetableLocationType &LocationType, TTimetableSequenceType &SequenceType, TTimetableShuttleLinkType &ShuttleLinkType, TNumList &ExitList, bool &Warning)
Parse a single timetable service action, return true for success.
Definition: TrainUnit.cpp:11092
TRailGraphics::LCPlain
Graphics::TBitmap * LCPlain
Definition: GraphicUnit.h:738
TTrain::LastActionDelayFlag
bool LastActionDelayFlag
used when trains join to ensure that there is a 30 second delay before the actual join takes place af...
Definition: TrainUnit.h:393
TTrainController::TotLatePassMins
float TotLatePassMins
Definition: TrainUnit.h:819
TTrainController::StripSpaces
void StripSpaces(int Caller, AnsiString &Input)
Strip both leading and trailing spaces at ends of Input and spaces before and after all commas and se...
Definition: TrainUnit.cpp:14063
FailMissedJoinOther
@ FailMissedJoinOther
Definition: TrainUnit.h:41
TTrain::JoinedOtherTrainFlag
bool JoinedOtherTrainFlag
true when the train has joined another train following an 'Fjo' timetable command or a signaller join...
Definition: TrainUnit.h:391
TTrainController::BFLow
bool BFLow
Definition: TrainUnit.h:801
TTrainController::TotArrDepPass
int TotArrDepPass
Definition: TrainUnit.h:842
SignallerPassRedSignal
@ SignallerPassRedSignal
Definition: TrainUnit.h:54
TRailGraphics::ChangeForegroundColour
void ChangeForegroundColour(int Caller, Graphics::TBitmap *BitmapIn, Graphics::TBitmap *BitmapOut, TColor NewForegroundColour, TColor BackgroundColour)
Definition: GraphicUnit.cpp:3423
TRailGraphics::CodeE
Graphics::TBitmap * CodeE
Definition: GraphicUnit.h:982
FailLockedRoute
@ FailLockedRoute
Definition: TrainUnit.h:40
TTrain::SignallerRemoved
bool SignallerRemoved
set when removed under signaller control to force a removal from the display at the next clock tick
Definition: TrainUnit.h:399
clB5G3R0
#define clB5G3R0
Definition: GraphicUnit.h:267
TTrain::FrontCodePtr
Graphics::TBitmap * FrontCodePtr
points to the front headcode segment, this is set to red or blue depending on TrainMode
Definition: TrainUnit.h:496
TOneRoute::SetRouteSignals
void SetRouteSignals(int Caller) const
Called when setting a route to set all points appropriately. Also called when a new train is added at...
Definition: TrackUnit.cpp:16815
Continuation
@ Continuation
Definition: TrackUnit.h:65
TTrainController::CallOnWarning
bool CallOnWarning
Definition: TrainUnit.h:787
TTrack::GetFilletGraphic
Graphics::TBitmap * GetFilletGraphic(int Caller, TTrackElement TrackElement)
Return a pointer to the point fillet (the bit that appears to move when points are changed) for the p...
Definition: TrackUnit.cpp:7580
GraphicUnit.h
TTrack::PointFlashFlag
bool PointFlashFlag
true when points are flashing during manual change
Definition: TrackUnit.h:746
TTrainController::OnTimeDeps
int OnTimeDeps
Definition: TrainUnit.h:835
TTrain::RestoreTimetableLocation
AnsiString RestoreTimetableLocation
stores the location name at which signaller control is taken, to ensure that it is back at that locat...
Definition: TrainUnit.h:470
TTrainController::TimetableIntegrityCheck
bool TimetableIntegrityCheck(int Caller, char *FileName, bool GiveMessages, bool CheckLocationsExistInRailway)
Checks overall timetable integrity, calls many other specific checking functions, returns true for su...
Definition: TrainUnit.cpp:10229
AllRoutes
TAllRoutes * AllRoutes
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:54
TTrain::SignallerMaxSpeed
int SignallerMaxSpeed
maximum train speed under signaller control (in km/h)
Definition: TrainUnit.h:362
NoEvent
@ NoEvent
Definition: TrainUnit.h:40
FailSplitDueToOtherTrain
@ FailSplitDueToOtherTrain
Definition: TrainUnit.h:40
TTrainController::SendPerformanceSummary
void SendPerformanceSummary(int Caller, std::ofstream &PerfFile)
At the end of operation a summary of overall performance is sent to the performance file by this func...
Definition: TrainUnit.cpp:19207
TTrain::ResetTrainElementID
void ResetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
After a train has moved off an element that element has its TrainIDOnElement value set back to -1 to ...
Definition: TrainUnit.cpp:3105
TRailGraphics::smMagenta
Graphics::TBitmap * smMagenta
Definition: GraphicUnit.h:897
RepairFailedTrain
@ RepairFailedTrain
Definition: TrainUnit.h:54
TTrainController::AllServiceCallingLocsMap
TAllServiceCallingLocsMap AllServiceCallingLocsMap
Definition: TrainUnit.h:770
TRailGraphics::smPaleGreen
Graphics::TBitmap * smPaleGreen
Definition: GraphicUnit.h:900
TRailGraphics::CodeS
Graphics::TBitmap * CodeS
Definition: GraphicUnit.h:996
TTrainController::ContinuationEntryFloatingTTString
AnsiString ContinuationEntryFloatingTTString(int Caller, TTrainDataEntry *TTDEPtr, int RepeatNumber, int IncrementalMinutes, int IncrementalDigits)
Build string for use in floating window for expected trains at continuations.
Definition: TrainUnit.cpp:9871
TFixedTrackPiece::SpeedTag
int SpeedTag
The element identification number - corresponds to the relevant SpeedButton->Tag.
Definition: TrackUnit.h:87
TTrain::FollowOnServiceRef
AnsiString FollowOnServiceRef
Definition: TrainUnit.h:325
TTrain::SPADFlag
bool SPADFlag
set when running past a red signal without permission flags to indicate relevant stop conditions or p...
Definition: TrainUnit.h:476
TTrain::Stopped
bool Stopped()
True if the train has stopped for any reason.
Definition: TrainUnit.h:669
TTrack::SigTableGroundSignal
TSigElement SigTableGroundSignal[40]
new at version 0.6 for ground signals
Definition: TrackUnit.h:723
TTrain::TrainToJoinIsAdjacent
bool TrainToJoinIsAdjacent(int Caller, TTrain *&TrainToJoin)
True for a train waiting to join another when the other train is adjacent.
Definition: TrainUnit.cpp:6569
TActionVectorEntry::ExitList
TNumList ExitList
the list of valid train exit TrackVector positions for 'Fer' entries (empty to begin with)
Definition: TrainUnit.h:138
TTrainController::OnTimeExits
int OnTimeExits
Definition: TrainUnit.h:837
TTrain::OldZoomOutElement
int OldZoomOutElement[3]
stores the Lead, Mid & Lag TrackVectorPositions, used for unplotting trains from the old position in ...
Definition: TrainUnit.h:483
TPrefDirElement::GetELink
int GetELink() const
Returns ELink.
Definition: TrackUnit.h:263
TDisplay::GetOutputLog8
TLabel * GetOutputLog8()
Definition: DisplayUnit.h:180
WaitingForJBO
@ WaitingForJBO
Definition: TrainUnit.h:43
TUtilities::CheckFileDouble
bool CheckFileDouble(std::ifstream &InFile)
checks that the value is a double, returns true for success
Definition: Utilities.cpp:331
TTrain::Straddle
TStraddle Straddle
the current Straddle value of the train (see TStraddle above)
Definition: TrainUnit.h:503
SignallerStop
@ SignallerStop
Definition: TrainUnit.h:54
TRailGraphics::CodeB
Graphics::TBitmap * CodeB
Definition: GraphicUnit.h:979
TAllRoutes::DiagonalFouledByRouteOrTrain
bool DiagonalFouledByRouteOrTrain(int Caller, int HLoc, int VLoc, int DiagonalLinkNumber)
The track geometry allows diagonals to cross without occupying the same track element,...
Definition: TrackUnit.cpp:19233
TTrain::RevisedStoppedAtLoc
bool RevisedStoppedAtLoc() const
Definition: TrainUnit.h:509
Display
TDisplay * Display
The object pointer for the on-screen display, object created in InterfaceUnit.
Definition: DisplayUnit.cpp:53
TTrainController::GetServiceFromVector
TTrainDataEntry GetServiceFromVector(AnsiString Caller, AnsiString ServiceReference, TTrainDataVector Vector, bool &FinishType, bool &FoundFlag)
Definition: TrainUnit.cpp:18461
TTrack::GetVLocMin
int GetVLocMin()
Definition: TrackUnit.h:877
TRailGraphics::Code3
Graphics::TBitmap * Code3
Definition: GraphicUnit.h:971
TUtilities::CheckFileBool
bool CheckFileBool(std::ifstream &InFile)
checks that the value is a bool returns true for success
Definition: Utilities.cpp:209
TTrack::OtherTrainOnTrack
bool OtherTrainOnTrack(int Caller, int NextPos, int NextEntryPos, int OwnTrainID)
True if another train on NextEntryPos track of element at NextPos, whether bridge or not,...
Definition: TrackUnit.cpp:10960
TTrain::MaximumPowerLimit
static const int MaximumPowerLimit
Watts (i.e. 100MW)
Definition: TrainUnit.h:315
TTrain::PlotStartPosition
void PlotStartPosition(int Caller)
Plots the train and sets up all relevant members for a new train when it is introduced into the railw...
Definition: TrainUnit.cpp:279
TTrack::TActiveTrackElementNameMapEntry
std::pair< AnsiString, int > TActiveTrackElementNameMapEntry
Definition: TrackUnit.h:702
TTrainController::SingleServiceOutput
void SingleServiceOutput(int Caller, int SSVectorNumber, TNumList MarkerList, TTrainDataVector &SingleServiceVector, std::ofstream &VecFile)
Outputs the single service vector for train direction analysis purposes in timetable conflict analysi...
Definition: TrainUnit.cpp:18354
TTrainController::TContinuationAutoSigEntry::SecondDelay
double SecondDelay
Definition: TrainUnit.h:711
TTrain::UnplotTrainInZoomOutMode
void UnplotTrainInZoomOutMode(int Caller)
Unplot train from screen in zoomed-out mode.
Definition: TrainUnit.cpp:8452
TTimetableFormatType
TTimetableFormatType
Timetable entry types.
Definition: TrainUnit.h:65
TTrainController::TrainFailedWarning
bool TrainFailedWarning
Flags to enable the relevant warning graphics to flash at the left hand side of the screen.
Definition: TrainUnit.h:787
TTrain::NextTrainID
static int NextTrainID
the ID value to be used for the next train that is created, static so that it doesn't need an object ...
Definition: TrainUnit.h:320
TTrainController::TLocServiceTimes
Class used for timetable conflict file compilation.
Definition: TrainUnit.h:756
TTrainOperatingData::RunningEntry
TRunningEntry RunningEntry
Definition: TrainUnit.h:187
NotAShuttleLink
@ NotAShuttleLink
Definition: TrainUnit.h:82
TRailGraphics::gl91set
Graphics::TBitmap * gl91set
Definition: GraphicUnit.h:721
TRailGraphics::Code_y
Graphics::TBitmap * Code_y
Definition: GraphicUnit.h:966
TAllRoutes::CheckMapAndRoutes
void CheckMapAndRoutes(int Caller)
Diagnostic function - checks equivalence for each route between entries in PrefDirVector and those in...
Definition: TrackUnit.cpp:18342
TAllRoutes::GetRouteElementDataFromRoute2MultiMap
TRouteElementPair GetRouteElementDataFromRoute2MultiMap(int Caller, int HLoc, int VLoc, TRouteElementPair &SecondPair)
Retrieve up to two TRouteElementPair entries from Route2MultiMap at H & V, the first as a function re...
Definition: TrackUnit.cpp:18298
TTrainController::TrainAdded
bool TrainAdded
true when a train has been added by a split (occurs outside the normal train introduction process)
Definition: TrainUnit.h:791
TActionVectorEntry::LocationName
AnsiString LocationName
Definition: TrainUnit.h:124
TTrainFormattedInformation::OneCompleteFormattedTrainVector
TOneCompleteFormattedTrainVector OneCompleteFormattedTrainVector
Definition: TrainUnit.h:282
ShuttleFinishedRemainingHere
@ ShuttleFinishedRemainingHere
Definition: TrainUnit.h:43
FailUnexpectedBuffers
@ FailUnexpectedBuffers
Definition: TrainUnit.h:41
Start
@ Start
Definition: TrainUnit.h:77
TRailGraphics::TempBackground
Graphics::TBitmap * TempBackground
Definition: GraphicUnit.h:904
TTrain::TimeTimeLocArrived
bool TimeTimeLocArrived
indicates whether has arrived (true) or not when ActionVectorEntryPtr->FormatType == TimeTimeLoc
Definition: TrainUnit.h:333
TActionType
TActionType
Used in LogAction when reporting a train action to the performance log & file.
Definition: TrainUnit.h:51
TTrainController::TContinuationAutoSigEntry::ThirdDelay
double ThirdDelay
Delays in seconds before consecutive signal changes - these correspond to the times taken for trains ...
Definition: TrainUnit.h:711
TTrain::LeadEntryPos
int LeadEntryPos
Definition: TrainUnit.h:366
TTrainController::LateArrivals
int LateArrivals
Definition: TrainUnit.h:829
TRailGraphics::CodeZ
Graphics::TBitmap * CodeZ
Definition: GraphicUnit.h:1003
NoShuttleLink
@ NoShuttleLink
Definition: TrainUnit.h:82
TTrainController::ReplotTrains
void ReplotTrains(int Caller, TDisplay *Disp)
plot all trains on the display
Definition: TrainUnit.cpp:9457
TTrainController::CheckShuttleServiceIntegrity
bool CheckShuttleServiceIntegrity(int Caller, TTrainDataEntry *TDEntryPtr, bool GiveMessages)
Check that each shuttle service ends either in Fns or Fxx-sh (though a single service can't end in Fx...
Definition: TrainUnit.cpp:14897
TTrain::FloatingTimetableString
AnsiString FloatingTimetableString(int Caller, TActionVectorEntry *Ptr)
Used in the floating window to display the timetable.
Definition: TrainUnit.cpp:7196
TAllRoutes::GetModifiableRouteAt
TOneRoute & GetModifiableRouteAt(int Caller, int At)
Returns a modifiable reference to the route at AllRoutesVector position 'At', after performing range ...
Definition: TrackUnit.cpp:17568
TTrain::NewShuttleFromNonRepeatService
void NewShuttleFromNonRepeatService(int Caller, bool NoLogFlag)
Carry out the actions needed when a new shuttle service is created from a non-repeating (F-nshs) serv...
Definition: TrainUnit.cpp:6626
TTrain::AValue
double AValue
< only true when a train has become a follow-on service early and the follow-on service normally pass...
Definition: TrainUnit.h:415
FailUnexpectedExitRailway
@ FailUnexpectedExitRailway
Definition: TrainUnit.h:41
TTrain::FrontTrainSplit
void FrontTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the front.
Definition: TrainUnit.cpp:5506
TTrainController::WithinTimeRange
bool WithinTimeRange(int Caller, AnsiString Time1, AnsiString Time2, int MinuteRange)
check whether the two times are within the range in minutes specified and return true if so....
Definition: TrainUnit.cpp:18497
TTrainController::WriteTrainsToImage
void WriteTrainsToImage(int Caller, Graphics::TBitmap *Bitmap)
Called by TInterface::SaveOperatingImage1Click) to write all trains to the image file.
Definition: TrainUnit.cpp:9475
TActionVectorEntry::LinkedTrainEntryPtr
TTrainDataEntry * LinkedTrainEntryPtr
link pointer for use between fsp/rsp & Sfs; Fjo & jbo; Fns & Sns; & all shuttle to shuttle links
Definition: TrainUnit.h:148
TTrainController::AtLocSuccessor
bool AtLocSuccessor(const TActionVectorEntry &AVEntry)
A shorthand function that returns true if the successor to a given timetable action command should be...
Definition: TrainUnit.cpp:13513
TTrain::ZeroPowerNoJoinedByMessage
bool ZeroPowerNoJoinedByMessage
Definition: TrainUnit.h:342
TTrainController::SaveSessionContinuationAutoSigEntries
void SaveSessionContinuationAutoSigEntries(int Caller, std::ofstream &SessionFile)
save ContinuationAutoSigEntries to a session file
Definition: TrainUnit.cpp:15546
TTrainOperatingData::TrainID
int TrainID
Definition: TrainUnit.h:185
TDisplay::GetOutputLog3
TLabel * GetOutputLog3()
Definition: DisplayUnit.h:155
NoLocation
@ NoLocation
Definition: TrainUnit.h:72
TRailGraphics::bmTransparentBgnd
Graphics::TBitmap * bmTransparentBgnd
Definition: GraphicUnit.h:914
TTrain::FirstHalfMove
bool FirstHalfMove
true when the train is on the first half of an element when it displays as fully on two elements....
Definition: TrainUnit.h:389
FailLocTooShort
@ FailLocTooShort
Definition: TrainUnit.h:40
TTrainDataEntry::Mass
int Mass
in kg
Definition: TrainUnit.h:217
NoMode
@ NoMode
Definition: TrainUnit.h:60
TTrainController::LatePasses
int LatePasses
Definition: TrainUnit.h:831
TTrainController::SplitRepeat
bool SplitRepeat(int Caller, AnsiString OneEntry, int &RearStartOrRepeatMins, int &FrontStartOrRepeatDigits, int &RepeatNumber, bool GiveMessages)
Parse a timetable repeat entry, return true for success.
Definition: TrainUnit.cpp:11931
TFixedTrackPiece::TrackType
TTrackType TrackType
the type of track element
Definition: TrackUnit.h:99
TTrainController::TLocServiceTimes::ArrTime
AnsiString ArrTime
Definition: TrainUnit.h:760
TTrainController::SSHigh
bool SSHigh
Definition: TrainUnit.h:801
TimeLoc
@ TimeLoc
Definition: TrainUnit.h:66
TActionVectorIterator
TActionVector::iterator TActionVectorIterator
iterator
Definition: TrainUnit.h:176
TPrefDirElement
Basic preferred direction or route element - track element with additional members.
Definition: TrackUnit.h:195
TTrainController::AvHoursIntValue
int AvHoursIntValue
Input in MTBFEditBox in timetable hours, min value is 1 and max is 10,000. Here because performance f...
Definition: TrainUnit.h:852
TTrainController::CrashedTrains
int CrashedTrains
Definition: TrainUnit.h:823
TTrainController::TTEditPanelVisible
bool TTEditPanelVisible
new at v2.6.0 so potential error message only shows in TTEdit mode
Definition: TrainUnit.h:799
TDisplay::WarningLog
void WarningLog(int Caller, AnsiString Statement)
Display warning message Statement in the bottom left hand warning position and scroll other messages ...
Definition: DisplayUnit.cpp:531
TTrain::LogAction
void LogAction(int Caller, AnsiString HeadCode, AnsiString OtherHeadCode, TActionType ActionType, AnsiString LocationName, TDateTime TimetableNonRepeatTime, bool Warning)
Send a message to the performance log and performance file, and if the message is flagged as a warnin...
Definition: TrainUnit.cpp:5150
TTrain::StoppedAtSignal
bool StoppedAtSignal
Definition: TrainUnit.h:478
TTrainController::GetControllerTrainTime
TDateTime GetControllerTrainTime(int Caller, TDateTime Time, int RepeatNumber, int IncrementalMinutes)
Get the interval between repeats.
Definition: TrainUnit.cpp:9859
TRailGraphics::Code_u
Graphics::TBitmap * Code_u
Definition: GraphicUnit.h:962
TTrackElement::Attribute
int Attribute
special variable used only for points, signals & level crossings, ignored otherwise; points 0=set to ...
Definition: TrackUnit.h:140
TStraddle
TStraddle
Defines the train position with respect to the track elements; three consecutive elements are Lead (f...
Definition: TrainUnit.h:297
FailEntryRouteSetAgainst
@ FailEntryRouteSetAgainst
Definition: TrainUnit.h:44
TTrain::TrainSkippedEvents
int TrainSkippedEvents
stores the pointer increment from the current action in ActionVector for skipped actions when a depar...
Definition: TrainUnit.h:377
TTrainController::THCandTrainPosParam
std::pair< AnsiString, int > THCandTrainPosParam
Definition: TrainUnit.h:772
TExitInfo::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:108
TTrainDataVector
std::vector< TTrainDataEntry > TTrainDataVector
vector class for containing the whole timetable - one entry per timetable service entry (the object i...
Definition: TrainUnit.h:240
TTrainController::GetRepeatTime
TDateTime GetRepeatTime(int Caller, TDateTime BasicTime, int RepeatNumber, int IncMinutes)
Return the repeating service time.
Definition: TrainUnit.cpp:14577
clCrashedBackground
#define clCrashedBackground
Definition: GraphicUnit.h:293
TTrack::IsLCAtHV
bool IsLCAtHV(int Caller, int HLoc, int VLoc)
True if a level crossing is found at H & V.
Definition: TrackUnit.cpp:7319
TTrain::OpTimeToAct
float OpTimeToAct
in minutes: new at v2.2.0 for operator time to act panel. Calculated in UpdateTrain,...
Definition: TrainUnit.h:444
TTrainController::TTClockTime
TDateTime TTClockTime
the time indicated by the timetable clock
Definition: TrainUnit.h:698
TAllRoutes::TLockedRouteClass::LockStartTime
TDateTime LockStartTime
the timetable time at which the route is locked, to start the 2 minute clock
Definition: TrackUnit.h:1613
TActionVectorEntry::SequenceType
TTimetableSequenceType SequenceType
indicates where in the sequence of codes the action lies
Definition: TrainUnit.h:144
TTrain::RearStartExitPos
int RearStartExitPos
the LinkPos value for the rear starting element (i.e. links to the front starting element)
Definition: TrainUnit.h:358
TTrainController::TLocServiceTimes::FrhMarker
AnsiString FrhMarker
Definition: TrainUnit.h:762
TRailGraphics::Code_o
Graphics::TBitmap * Code_o
Definition: GraphicUnit.h:956
TRailGraphics::CodeV
Graphics::TBitmap * CodeV
Definition: GraphicUnit.h:999
TTrainController::TLocServiceTimes::ServiceAndRepeatNum
AnsiString ServiceAndRepeatNum
Definition: TrainUnit.h:758
TTrain::SetTrainElementID
void SetTrainElementID(int Caller, unsigned int TrackVectorPosition, int EntryPos)
When a train moves onto an element that element has its TrainIDOnElement value set to the TrainID val...
Definition: TrainUnit.cpp:3069
TActionVectorEntry::NonRepeatingShuttleLinkHeadCode
AnsiString NonRepeatingShuttleLinkHeadCode
string values for timetabled event entries, null on creation
Definition: TrainUnit.h:124
TActionVectorEntry::ShuttleLinkType
TTimetableShuttleLinkType ShuttleLinkType
indicates whether or not the action relates to a shuttle service link
Definition: TrainUnit.h:146
TTrain::TimetableFinished
bool TimetableFinished
set when there are no more timetable actions
Definition: TrainUnit.h:409
TUtilities::CallLog
std::deque< AnsiString > CallLog
call stack store, saved to the errorlog for diagnostic purposes
Definition: Utilities.h:55
TRailGraphics::CodeN
Graphics::TBitmap * CodeN
Definition: GraphicUnit.h:991
TActionVectorEntry::Command
AnsiString Command
Definition: TrainUnit.h:124
TTrain::RepeatShuttleOrRemainHere
void RepeatShuttleOrRemainHere(int Caller, bool NoLogFlag)
Carry out the actions needed to create either a new shuttle service or (if all repeats have finished)...
Definition: TrainUnit.cpp:6670
TTrain::CallingOnAllowed
bool CallingOnAllowed(int Caller)
True if the train can be called on at its current position - see detail in .cpp file.
Definition: TrainUnit.cpp:4775
TTrainController::TContinuationTrainExpectationEntry::HeadCode
AnsiString HeadCode
service headcode
Definition: TrainUnit.h:731
TTrackElement::Length23
int Length23
Definition: TrackUnit.h:148
TTrain::SignallerStoppingFlag
bool SignallerStoppingFlag
set when the signaller stop command has been given
Definition: TrainUnit.h:401
TTrainController::NotStartedTrainLateArr
int NotStartedTrainLateArr
total number of arrivals & departures for trains that haven't started yet for locations not reached y...
Definition: TrainUnit.h:848
TUtilities::LoadFileBool
bool LoadFileBool(std::ifstream &InFile)
loads a bool value from the file
Definition: Utilities.cpp:145
TTrainController::SigSLow
bool SigSLow
Message flags in TT checks to stop being given twice.
Definition: TrainUnit.h:801
TRailGraphics::Code_m
Graphics::TBitmap * Code_m
Definition: GraphicUnit.h:954
TTrack::AnyLinkedBarrierDownVectorManual
bool AnyLinkedBarrierDownVectorManual(int Caller, int HLoc, int VLoc, int &BDVectorPos)
Checks BarrierDownVector and returns true if there is one that is linked to the LC at H & V positions...
Definition: TrackUnit.cpp:6343
TRailGraphics::gl92set
Graphics::TBitmap * gl92set
Definition: GraphicUnit.h:723
SNSShuttle
@ SNSShuttle
Definition: TrainUnit.h:66
RemoveTrain
@ RemoveTrain
Definition: TrainUnit.h:53
TTrainController::TLocServiceTimes::DepTime
AnsiString DepTime
Definition: TrainUnit.h:761
TTrainController::SetWarningFlags
void SetWarningFlags(int Caller)
This sets all the warning flags (CrashWarning, DerailWarning etc) to their required states after a se...
Definition: TrainUnit.cpp:19634
TDisplay::PlotSmallOutput
void PlotSmallOutput(int Caller, int HPos, int VPos, Graphics::TBitmap *PlotItem)
Plot small (4x4) graphic PlotItem on the zoomed-out display at HPos & Vpos.
Definition: DisplayUnit.cpp:113
TTrainController::TContinuationTrainExpectationEntry
Class that stores data for trains expected at continuation entries (kept in a multimap - see below),...
Definition: TrainUnit.h:727
SNSNonRepeatFromShuttle
@ SNSNonRepeatFromShuttle
Definition: TrainUnit.h:66
TTrainController::TotEarlyPassMins
float TotEarlyPassMins
Definition: TrainUnit.h:815
TRailGraphics::CodeH
Graphics::TBitmap * CodeH
Definition: GraphicUnit.h:985
TTrainController::OpTimeToActMultiMap
TOpTimeToActMultiMap OpTimeToActMultiMap
added v2.2.0 for Op time to act display
Definition: TrainUnit.h:866
TTrainController::TContinuationAutoSigEntry::AccessNumber
int AccessNumber
the number of times the signal changing function has been accessed - starts at 0 and increments after...
Definition: TrainUnit.h:713
TActionVectorEntry::SignallerControl
bool SignallerControl
indicates a train that is defined by the timetable as under signaller control
Definition: TrainUnit.h:128
TTrain::GetTrainTime
TDateTime GetTrainTime(int Caller, TDateTime Time)
Returns the timetable action time corresponding to 'Time' for this train, i.e. it adjusts the time va...
Definition: TrainUnit.cpp:5055
TTrain::BrakeRate
double BrakeRate
the current train brake rate
Definition: TrainUnit.h:433
TTrackElement::SpeedLimit23
int SpeedLimit23
Element lengths and speed limits, ...01 is for the track with link positions [0] and [1],...
Definition: TrackUnit.h:148
TTrainController::TContinuationAutoSigEntry::PassoutTime
TDateTime PassoutTime
the timetable clock time at which the train exits from the continuation
Definition: TrainUnit.h:717
TTrain::FinishJoinLogSent
bool FinishJoinLogSent
Definition: TrainUnit.h:337
TTrack::GapFlashFlag
bool GapFlashFlag
true when a pair of connected gaps is flashing
Definition: TrackUnit.h:734
TRailGraphics::gl95set
Graphics::TBitmap * gl95set
Definition: GraphicUnit.h:727
TrainUnit.h
PassTime
@ PassTime
Definition: TrainUnit.h:67
TTrainController::IncorrectExits
int IncorrectExits
Definition: TrainUnit.h:828
TTrain::ContinuationExit
bool ContinuationExit(int Caller, int Element, int Exitpos) const
True if Element is a continuation and Exitpos is the continuation end.
Definition: TrainUnit.cpp:2993
TFixedTrackPiece::Link
int Link[4]
Track connection link values, max. of 4, unused = -1, top lh diag.=1, top=2, top rh diag....
Definition: TrackUnit.h:89
TTrain::UpdateTrain
void UpdateTrain(int Caller)
Major function called at each clock tick for each train & handles all train movement & associated act...
Definition: TrainUnit.cpp:644
TRailGraphics::LockedRouteCancelPtr
Graphics::TBitmap * LockedRouteCancelPtr[10]
for locked route cancel graphic, 1 for each of 8 links, 0 & 5 included as for direction
Definition: GraphicUnit.h:1052
TRailGraphics::Code8
Graphics::TBitmap * Code8
Definition: GraphicUnit.h:976
TTrainController::SameDirection
bool SameDirection(int Caller, AnsiString Ref1, AnsiString Ref2, AnsiString Time1, AnsiString Time2, int RepeatNum1, int RepeatNum2, TServiceCallingLocsList List1, TServiceCallingLocsList List2, AnsiString Location, bool Arrival)
Determines whether two services are running in the same direction when they arrive or depart from Loc...
Definition: TrainUnit.cpp:18781
TTrain::TrainToBeJoinedByIsAdjacent
bool TrainToBeJoinedByIsAdjacent(int Caller, TTrain *&TrainToBeJoinedBy)
True for a train waiting to be joined when the joining train is adjacent.
Definition: TrainUnit.cpp:6597
TTrain::SetTrainMovementValues
void SetTrainMovementValues(int Caller, int TrackVectorPosition, int EntryPos)
Calculates train speeds and times for the element that the train is about to enter....
Definition: TrainUnit.cpp:3489
TActionVectorEntry::Warning
bool Warning
if set triggers an alert in the warning panel when the action is reached
Definition: TrainUnit.h:130
TTrain::PickUpBackgroundBitmap
void PickUpBackgroundBitmap(int Caller, int HOffset, int VOffset, int Element, int EntryPos, Graphics::TBitmap *GraphicPtr) const
Store the background bitmap pointer (BackgroundPtr - see above) prior to being overwritten by the tra...
Definition: TrainUnit.cpp:2581
clStoppedTrainInFront
#define clStoppedTrainInFront
Definition: GraphicUnit.h:302
TDisplay::GetOutputLog10
TLabel * GetOutputLog10()
Definition: DisplayUnit.h:190
TTrainDataEntry::ServiceReference
AnsiString ServiceReference
Definition: TrainUnit.h:209
TTrain::SignallerChangeTrainDirection
void SignallerChangeTrainDirection(int Caller)
Unplots & replots train, which checks for facing signal and sets StoppedAtSignal if req'd.
Definition: TrainUnit.cpp:6924
TTrain::ZeroPowerNoRearSplitMessage
bool ZeroPowerNoRearSplitMessage
Definition: TrainUnit.h:340
FailMissedChangeDirection
@ FailMissedChangeDirection
Definition: TrainUnit.h:42
NotSet
@ NotSet
Definition: TrackUnit.h:75
Repeat
@ Repeat
Definition: TrainUnit.h:67
TRailGraphics::Code_c
Graphics::TBitmap * Code_c
Definition: GraphicUnit.h:944
TTrainController::StripExcessFromHeadCode
void StripExcessFromHeadCode(int Caller, AnsiString &HeadCode)
change an extended headcode to an ordinary 4 character headcode
Definition: TrainUnit.cpp:13522
TTrack::TIMPair
std::pair< unsigned int, unsigned int > TIMPair
TrackElement pair type used for inactive elements, values are vector positions.
Definition: TrackUnit.h:670
TUtilities::Clock2Stopped
bool Clock2Stopped
when true the main loop - Interface->ClockTimer2 - is stopped
Definition: Utilities.h:38
TTrainController::MRSHigh
bool MRSHigh
Definition: TrainUnit.h:801
TTrackElement::GroundSignal
@ GroundSignal
Definition: TrackUnit.h:157
LevelCrossing
@ LevelCrossing
Definition: TrackUnit.h:66
TDisplay::GetOutputLog2
TLabel * GetOutputLog2()
Definition: DisplayUnit.h:150
TTrain::NotInService
bool NotInService
Definition: TrainUnit.h:479
TRailGraphics::CodeA
Graphics::TBitmap * CodeA
Definition: GraphicUnit.h:978
TTrack::ActiveTrackElementNameMapCompiledFlag
bool ActiveTrackElementNameMapCompiledFlag
indicates that the ActiveTrackElementNameMap has been compiled
Definition: TrackUnit.h:728
TTrainController::LocServiceTimesLocationSort
bool LocServiceTimesLocationSort(TLocServiceTimes i, TLocServiceTimes j)
Definition: TrainUnit.h:877
TOneTrainFormattedEntry
A single train timetable action for use in a formatted timetable.
Definition: TrainUnit.h:248
TTrain::RearTrainSplit
void RearTrainSplit(int Caller)
Carry out the actions needed when a train is to split from the rear.
Definition: TrainUnit.cpp:5816
TTrainController::ConsolidateSARNTArrDep
AnsiString ConsolidateSARNTArrDep(int Caller, const AnsiString Input, int &NumTrainsAtLoc, AnsiString Location, bool Arrival, bool &AnalysisError, int &MaxNumberOfSameDirections)
Removes duplicates from and sorts ServiceAndRepeatNumTotal into alphabetical order for arrivals (bool...
Definition: TrainUnit.cpp:18523
Track
TTrack * Track
the object pointer, object created in InterfaceUnit
Definition: TrackUnit.cpp:53
TTrackElement::Conn
int Conn[4]
Connecting element position in TrackVector, set to -1 if no connecting link or if track not linked.
Definition: TrackUnit.h:142
Signaller
@ Signaller
Definition: TrainUnit.h:60
TTrain::TrainCrashedInto
int TrainCrashedInto
the TrainID of the train that this train has crashed into, recorded so that train can be marked and d...
Definition: TrainUnit.h:489
TRailGraphics::smYellow
Graphics::TBitmap * smYellow
Definition: GraphicUnit.h:902
RailGraphics
TRailGraphics * RailGraphics
the object pointer, object created in InterfaceUnit
Definition: GraphicUnit.cpp:50
TTrainOperatingData
Data for a specific train for use during operation.
Definition: TrainUnit.h:183
FailCreatePoints
@ FailCreatePoints
Definition: TrainUnit.h:40
TRailGraphics::smBlack
Graphics::TBitmap * smBlack
Definition: GraphicUnit.h:890
TTrain::TreatPassAsTimeLocDeparture
bool TreatPassAsTimeLocDeparture
< indicates failure
Definition: TrainUnit.h:413
Bridge
@ Bridge
Definition: TrackUnit.h:65
TRailGraphics::Code_p
Graphics::TBitmap * Code_p
Definition: GraphicUnit.h:957
TTrainFormattedInformation::NumberOfTrains
int NumberOfTrains
number of repeats + 1
Definition: TrainUnit.h:280
TTrain::LagExitPos
int LagExitPos
TrackVector positions, & entry & exit connection positions for the elements that the train occupies.
Definition: TrainUnit.h:366
TTimetableSequenceType
TTimetableSequenceType
Definition: TrainUnit.h:76
TTrain::TimetableMaxRunningSpeed
double TimetableMaxRunningSpeed
the maximum train running speed when in timetable mode (see int SignallerMaxSpeed for signaller contr...
Definition: TrainUnit.h:425
SequTypeForRepeatEntry
@ SequTypeForRepeatEntry
Definition: TrainUnit.h:77
TRailGraphics::bmName
Graphics::TBitmap * bmName
Definition: GraphicUnit.h:524
clB0G0R0
#define clB0G0R0
Definition: GraphicUnit.h:36
Buffers
@ Buffers
Definition: TrackUnit.h:65
TActionVectorEntry::RearStartOrRepeatMins
int RearStartOrRepeatMins
Definition: TrainUnit.h:134
clFrontCodeTimetable
#define clFrontCodeTimetable
Definition: GraphicUnit.h:296
TActionVectorEntry::OtherHeadCode
AnsiString OtherHeadCode
Definition: TrainUnit.h:124